Skip to content

raandree/copilot-atelier

v4.0.0MIT

Portable GitHub Copilot customization library: role-specific custom agents with handoffs and subagent allow-lists, deterministic lifecycle hooks, and on-demand Agent Skills for PowerShell/DSC engineering (Sampler, Pester, Datum, AutomatedLab, WinRM, MECM), document conversion (PDF, DOCX, XLSX, Marp, pandoc), Outlook and Microsoft To Do automation, research and citation integrity, agentic-security review, and skill/prompt/agent evaluation. Skills are portable; agents, rules, slash commands, and hooks load in GitHub Copilot clients. Keybindings are not a plugin component type; install the CopilotAtelier module from the PowerShell Gallery to get those too.

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

Removed

  • The .github/hooks smoke-test probe, which had been failing on every turn since it was committed (2026-09-02). stop-probe.json and Test-HookLoaded.ps1 were scratch: a Stop hook that appended one line to %TEMP%\workspace-hook-probe.log to prove the workspace hook location loads at all. They answered that question on 2026-08-10 and the answer is written into com.github.copilot/hooks/README.md and the changelog entry below — the files themselves had no further job.

    They were not merely idle. The windows override hardcoded D:\Git\CopilotAtelier\.github\hooks\Test-HookLoaded.ps1, the drive the repository sat on when the probe was written, and on Windows that override wins. Every turn on any other machine ended with "The argument … to the -File parameter does not exist". The POSIX command was no better in principle: ./.github/hooks/Test-HookLoaded.ps1 is relative, and the same README says VS Code does not guarantee the working directory, which is why every shipped hook resolves its own path.

    Nothing caught it because nothing looked. The Hook configuration suite in tests/Hooks.Tests.ps1 — which asserts exactly this, that a hook command resolves to a script that exists and carries no shell-interpolated token — is scoped to com.github.copilot/hooks/hooks.json. A second hook file one directory away was outside every gate the repository owns. That suite now enumerates every tracked *.json sitting directly inside a folder named hooks and requires the shipped configuration to be the only one, so the next stray hook file fails the build instead of the chat. The guard was proven by planting one and watching it go red.

Added

  • Add tools/plan-review, an optional local review surface for a Design Concept: it renders the Markdown and its Mermaid diagrams, anchors comments to stable sections, and records a verdict against one specific revision hash. It is opt-in in the strong sense — it is absent from CustomizationDirectory, so the built module and a Gallery install never carry it; no PowerShell source references it; and its Node dependencies are installed explicitly by whoever wants the feature, never by an install, an update, or a validation run.

    A browser verdict is feedback, not sign-off, and the code says so rather than the documentation. An HTTP request proves that something holding the session cookie and the CSRF token posted a content hash; it proves neither identity nor authority to start implementation. Every verdict is therefore persisted with authority: "local-http-feedback" beneath a store header of approvalAuthority: "chat-sign-off-required", the header is repeated on the response and shown above the document, and there is no endpoint that writes a Decision record, triggers a handoff, or runs a command. A --state root resolving inside .memory-bank/decisions is refused at launch. The existing chat sign-off in the Software Architect workflow stays the only thing that authorizes implementation, and the agent body now says that where it points at the tool.

    Revision hashes cover the original document bytes. Comments retain section identity; ambiguous duplicate headings require one exact content match and otherwise stay unanchored. A section key is unique across the whole document, not merely per heading slug: an occurrence ordinal on its own issues risks-2 twice for Risks, Risks, Risks 2, and two sections sharing a key anchor a comment to the wrong heading. Section splitting follows the CommonMark fence rules, so a shorter fence nested inside a longer one cannot expose a fake heading and mis-anchor a comment. Verdict dialogs retain the document and revision they displayed, so a background refresh cannot approve newer content. Requests against stale hashes are refused with 409, and the hash is checked a second time inside the serialized store write — a file edited while the request waits for the lock is refused rather than approved for the bytes it no longer has. The source is read once more after the commit, so a change landing in that last window is reported as superseded instead of being presented as current approval.

    Loopback binding is treated as a reachability reduction, not an authorization boundary. A non-loopback bind address is refused outright, and the allowed authority follows the address actually bound, so ::1 produces [::1]:<port> rather than a hard-coded 127.0.0.1. Every request must carry a Host matching the bound authority; every mutation must additionally carry the exact server Origin, a Sec-Fetch-Site of same-origin or none when the browser sends one, a JSON content type, the per-launch session cookie, and a matching X-CSRF-Token. The session secret is generated per launch and never persisted, so a cookie minted by an earlier server is rejected by the next one even when it reuses the same feedback store. Because cookies are scoped by host and not by port, the cookie name carries a per-launch identifier: opening a second review server in the same browser no longer signs the first one out, and neither server accepts the other's cookie.

    Documents are authorized at launch and addressed on the wire by an opaque sixteen-character identifier, so no request parameter ever names a path. There is no directory listing, no URL fetcher, no shell endpoint, and no generic static handler — vendor assets come from an exact filename allow-list mapped onto node_modules. Every path is realpath-resolved, required to sit inside the declared root, and rejected when any ancestor from the root down is a symbolic link or junction, and the check runs again at read time rather than only at launch, so a link swapped in afterwards still fails.

    Rendering disables raw HTML at the parser instead of filtering it afterwards: markdown-it runs with html: false, and DOMPurify then applies a tag, attribute, and URI allow-list that admits only http, https, and mailto. An image is never fetched — its alternative text is rendered instead, because an image is an implicit external load. Mermaid runs client-side with securityLevel: 'strict' and its SVG is sanitized again before insertion. Responses carry Content-Security-Policy: default-src 'none' with script-src 'self' and no unsafe-eval. The page's own stylesheet, script, and vendor bundles are snapshotted at launch and served from memory, so an asset deleted or swapped afterwards can neither change what the page runs nor leave a request hanging on a broken read.

    Bodies cap at 64 KiB, comment text at 4000 characters, notes at 2000, comments at 200 per document, and documents at 1 MiB. The store is read under a 2 MiB byte bound and fully validated — schema, document identity, every comment field, and the verdict, including its authority, which a stored file can therefore never use to promote itself to sign-off, and every hash, which must be a lowercase SHA-256 digest rather than any bounded string. The write path enforces the same byte bound on the serialized UTF-8 payload, because the count and length bounds do not imply it: 4000 characters of multibyte text cost up to three bytes each, so 200 legal comments could otherwise produce a file the next read refuses. A write that would cross the bound is refused as store-capacity before the temporary file exists, and the stored feedback is left unchanged. A store file that fails any of those checks is reported and left byte-for-byte intact, and the next mutation is refused rather than overwriting somebody's pending review; recovery is a deliberate act by the operator. The exclusive write lock records its owning process: a lock held by a live process is waited on and then refused, a lock is reclaimed only when its named owner is provably gone, the reclaim removes the entries this tool wrote rather than deleting a directory tree it does not own, and a mutation that loses ownership refuses to commit. Server lifetime is bounded by --ttl, and Ctrl+C, the page's Stop server button, and the printed process id all stop it cleanly.

    Add revision-scoped draft recovery, retryable connection errors, an authorized-document selector, and wrapping mobile status text. A draft written against a revision or a section that is no longer current is never re-attached to new content: it is listed under Unsent drafts from an earlier revision with the section and revision it was written on, for explicit discard, and a pending verdict note survives a stale refusal. Switching documents takes a request ticket, so a slow response for one document cannot render under another document's actions. The section outline is a disclosure that starts collapsed on a narrow viewport, and the permanent keyboard tutorial line is gone — the shortcuts remain, named in tooltips and announced to assistive technology. Apply input limits at launch and reload, reject invalid UTF-8, and reject linked feedback roots before reads or writes. Portable Node test commands and desktop/mobile browser regressions cover these boundaries. The ordinary repository gate runs dependency-free Node tests when Node is available and never installs npm dependencies.

    Documented in docs/plan-review.md, with the trust analysis in docs/plan-review-threat-model.md. Rollback is deletion: nothing else in the repository depends on it.

  • Add read-only Get-CopilotAtelierClientAdapter, a thin compatibility adapter that reports how a Custom agent profile is composed for each supported Copilot client and, more importantly, what that client cannot do. The VS Code files under com.github.copilot/agents stay the only source of every shared workflow; the composed body is byte-identical, and only frontmatter is rewritten, so there is no second catalog to drift.

    Discovery is not parity, and the gap is specific. The published custom agents configuration that the Copilot CLI follows documents one model string rather than a priority array, a closed set of tool aliases rather than product-qualified tool identifiers, and no subagent allow-list, handoff, or argument hint — and it ignores an unrecognized tool name, so a profile that loads there can quietly lose the capabilities its own body depends on. The representative profile alone declares dozens of identifiers with no client equivalent. The scope is VS Code Copilot Chat and the Copilot CLI; no other client was checked, and none is claimed.

    Four rules are enforced by tests rather than promised in prose. Every mapping is explicit, so an identifier absent from the allow-list is an error rather than a silent drop, and frontmatter is parsed as a strict YAML subset that rejects an unknown top-level field, a duplicate field, a block scalar, an anchor, an alias, a tag, an unterminated list, or an unbalanced quote — a tool list is never partially mapped, and a new safety field cannot disappear without a diagnostic. Nothing is widened to make a workflow run: only a tool that genuinely starts a command may become the shell-execution alias, and the contract names those explicitly, because a product prefix such as execute/ is a namespace rather than proof of execution authority — reading an existing terminal buffer, running a declared VS Code task, and running tests all stay unsupported instead of being traded for a terminal. Every other mapping has to stay inside the capability class of its source identifier. And a restriction that cannot be expressed removes what it guards — the subagent allow-list has no counterpart, so the composed variant loses the delegation tool instead of inheriting unbounded delegation, and the model field is omitted rather than translated into an invented client model identifier. A capability the shared body declares mandatory that cannot be mapped fails the composition instead of shipping without it.

    A workflow the client cannot run is refused rather than degraded, and the refusal travels inside the file. The shared engineering body offers review: on and cycle: full, both satisfied in VS Code by dispatching the security-reviewer subagent and by advancing through handoffs, and the Copilot CLI provides neither. The composition therefore prepends an additive client-limitation section naming both modes unavailable and instructing the agent to refuse the request and return it to VS Code Copilot Chat; a required independent review is never quietly downgraded to a written recommendation. The section is composed presentation, not an edit to the shared body, and its boundary says so: explicit begin and end markers, the SHA-256 of the shared body carried in the end marker, and the authoritative body last in the file. Get-CopilotAtelierClientAdapter -RequiredWorkflow throws instead of returning content when a caller depends on a mode the client cannot honour.

    The rollout is one profile wide: only software-engineer is adapted, and any other profile is refused until it has its own passing compatibility test. Neither client is reported as runtime verified, because no client session backs these mappings: both are StructurallyChecked against current documentation. The Copilot CLI is not installed here, and the observation that the source profile loaded in VS Code 1.136.1 is kept as historical source-profile evidence about that file rather than as a property of an arbitrary composed variant.

    The composed variants are a build artifact written to output/clientAdapters/<client>/ by the new Build_Client_Adapter_Variants task, and they are deliberately not deployed — not in the module payload, not in the built module, not written to the canonical target, and not published through the plugin channel. Two profiles for one agent inside ~/.copilot/agents, which VS Code and the Copilot CLI both read, would be a duplicate discovery entry rather than a compatibility fix. Tests check the artifact for staleness byte for byte, reject a leftover variant for a profile no longer adapted, and assert the packaging isolation. The build task owns that directory rather than sweeping it, and the bound is three separate properties rather than one marker file. The whole operation — every variant, the manifest schema, its list shape, every entry, duplicate manifest paths, duplicate variant destinations, and every collision — is constructed and validated before the first delete, so a request that is going to be refused leaves the directory exactly as it was found; validating and deleting one entry at a time would already have destroyed a valid first entry by the time a later unsafe one was caught. Every path component is checked by the shared regular-path guard before it is read, deleted, created, written, or enumerated: the output root, the artifact directory, the ownership manifest, each client directory, each generated file, and each destination, because a link in the middle of the path redirects a delete and a write just as effectively as one at either end. And ownership is proved by content, not by a file name: .copilot-atelier-adapter-manifest.json records the SHA-256 of every file it generated, so a generated file edited in place is refused rather than silently deleted or overwritten, and a file at a destination this build never generated is refused rather than adopted — whatever it contains, because an identical unowned file is the same silent ownership grab as a different one. A names-only schema 1 manifest cannot prove any of that, so it is refused with the paths it claims and a migration path rather than adopting the hashes of whatever now sits there. A directory without the marker, a reserved build directory such as module or RequiredModules, and a path that is not a direct child of the build output stay refused — a mistyped ClientAdapterSubdirectory can no longer take unrelated build output with it. Authored behavioral evaluation cases are in docs/client-adapter-evals.md; none has been executed, because every one of them needs a model-backed client session.

  • Add the changed-file-validation Skill: an opt-in, bounded validation pass over the files one work batch actually changed. Collection is manual and explicit — Add-ChangedFile.ps1 records the paths it is given, deduplicates repeated edits onto one entry with an occurrence count, and normalizes absolute, backslash, and forward-slash forms onto the same project-relative entry. Batches are isolated by session identifier, and the identifier is restricted to letters, digits, period, underscore, and hyphen so it can never name a path. Concurrent collectors merge rather than overwrite: a writer re-reads the store under a bounded lock before it writes, so two sessions collecting at the same moment cannot lose a file.

    No hook is wired, and the reason is recorded rather than assumed. Of the documented client events, PostToolUse is the only plausible collector and its edit-tool input contract is not verified for this implementation. The shipped hooks are also mandatory — PreToolUse blocks remote mutation and Stop closes the session clock — so an optional collector inside either one would turn a validation fault into a guard fault and give Stop a way to fail and be retried. The hook configuration is therefore unchanged, still declares exactly PreToolUse, SessionStart, Stop, and PreCompact, and carries no reference to changed-file collection; a regression asserts that, that no hook script gained the dependency, and that the remote-mutation guard still exits 2 on a push.

    Invoke-ChangedFileValidation.ps1 is the explicit entry point, and it reuses what the repository already has rather than inventing a build system. PowerShell parsing through Parser::ParseFile and PSScriptAnalyzer both run in an owned child worker with a wall clock, because an in-process validator has no wall clock at all and a pathological file or a wedged analyzer would take the session with it; the worker receives its request over standard input, never dot-sources or otherwise runs the file it checks, and is handed an inline analyzer settings hashtable so no project PSScriptAnalyzerSettings.psd1 and no custom rule module is loaded. One validation run executes per session at a time.

    Markdown is checked by a real markdownlint or not at all. Markdown.NativeStructure implements four rules — MD047, unterminated frontmatter, an unterminated code fence, and invalid UTF-8 or a stray control character — and is recorded with coverage=partial, because .markdownlint.jsonc never sets default: false and therefore leaves most markdownlint rules enabled and uncovered here. Markdown.Lint is consequently always part of the plan for a markdown file: without a linter it is Unavailable and the entry stays unverified, rather than being verified by a native check that is not markdownlint. Only the markdownlint-cli interface is driven, and only after --version answers with a version; markdownlint-cli2 is reported UnsupportedInterface rather than guessed at. The declarative configuration is copied in beside the snapshot and passed explicitly, which is also what disables the linter's nested and ancestor discovery, and a .js, .cjs, .mjs, or markdownlint-cli2 configuration anywhere from the file's directory up to the project root makes the check unavailable instead of being handed to a linter that would execute it. What the chosen declarative configuration contains is decided by parsing it, not by scanning its bytes: a text scan is not a boundary, because JSON can spell extends with Unicode escapes. JSON and JSONC are read by a non-executing parser and checked against a conservative rule-map schema, so a dynamic include, a custom rule, a module path at any depth, an unknown key, and an unexpected shape are all refused before the version probe or the linter starts. A format with no trusted parser here — YAML, and JSONC on a host without a comment-tolerant JSON reader, which includes Windows PowerShell 5.1 — is reported ConfigurationFormatUnsupported rather than copied to an executable unread.

    A receipt is a claim about exact bytes checked under an exact plan. Validators read an isolated snapshot: the bytes are copied into a generated directory under a generated, metacharacter-free name, and that copy is what is hashed and checked, so the receipt is bound to the byte sequence a validator actually read rather than to a path that may have moved underneath it. Alongside the per-check validator, executable, version, configuration identity, outcome, exit status, and up to twenty located diagnostics, the receipt records a validation plan identity — a SHA-256 over the checks the file is due, resolved from the extension, -FailOnSeverity, the installed PSScriptAnalyzer version, the SHA-256 of the linter entry point, the bytes of the declarative markdown configuration, and the SHA-256 of the shipped code that performs each check. Hashing the entry point rather than measuring it is what catches a linter replaced in place at the same length with its timestamp preserved, and hashing the shipped worker and helpers is what stops a receipt outliving an edit to the checker itself. That identity is bounded and says so: it does not reach PSScriptAnalyzer's rule implementations beyond their module version, and it does not reach the dependency tree under an npm wrapper, because no supported interface exposes one to a process-free read. Reuse requires all of it to hold: intact shape, results and fields this implementation actually writes, a Boolean change flag by type rather than by coercion, a hash matching the bytes on disk, no change flagged during the run, an identical plan identity, exactly the planned checks with no duplicate and no extra, each carrying the identity the plan names and an exit status that agrees with its result, and a recorded outcome equal to what those checks add up to. A result this implementation cannot produce is treated as unavailable rather than counted towards a pass. Changing -FailOnSeverity, selecting, upgrading, or replacing a linter, upgrading PSScriptAnalyzer, editing the checker, editing .markdownlint.jsonc, and hand-editing the store all invalidate reuse instead of inheriting a pass, and Get-ChangedFileBatch.ps1 applies the same rule without starting a process, because nothing in the plan identity needs one. Deleted, renamed, and unsupported files are reported as Missing and Unsupported and are never verified.

    Every bound is explicit and every failure is fail-closed. The input-size bound is enforced before any content is read or hashed. External output is drained and capped while the child is still running rather than read to end afterwards, so a talkative or hostile tool cannot grow the retained buffer without limit and neither stream can deadlock on the other; a child that fails to start, one that outruns the bound, and one whose descendants hold the pipe open past the drain grace are reported as Unavailable, TimedOut, or incomplete, with the exit status left null when it is genuinely unknown, and none of them can produce a pass. A file name is data: because every argument is a name this workflow generated and the working directory is the snapshot directory, no project text reaches a command line at all — which matters on Windows, where a markdownlint entry point is a .cmd shim the command processor re-parses. Path containment is enforced by walking every existing directory from the selected project root down to the file before each read and each write, and again at validation time because a batch collected minutes ago may have gained a link since. A timed-out child is stopped together with the descendants it spawned and nothing else.

    Nothing outside the batch store is written: no file it validates and no file it was not given is rewritten, the report writes nothing at all, and Test-CopilotAtelier runs no validator from here. The store lives at .copilot-atelier/changed-file-validation/batches.json, is replaced atomically under the lock, and refuses a store recording another project root, an unsupported schema version, or unparsable JSON. The Skill documents in its own body that it supplements immediate behavior-scoped tests and the full build gate rather than delaying or replacing either. evals/validation-cases.json carries offline cases only, labelled authored with executed set to false, because no model-backed sweep has been run for it. The Skill joins the engineering installation profile.

  • Add read-only Get-CopilotAtelierSkillHealth, an on-demand Skill maintenance report that combines trustworthy usage observations, the evaluation artifacts that already exist, and structural checks, and suggests what a human should investigate, improve, consolidate, or review for retirement without ever changing a Skill, a setting, or an installation. The supported client event contract was established before anything was built: the documented hook events are SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PreCompact, SubagentStart, SubagentStop, and Stop, and not one of them reports that a Skill was selected, loaded, or executed. There is therefore no supported event to observe a Skill activation from, no automatic collection is implemented, capture is disabled by default, and the report names that gap in its output and its help rather than papering over it. No session log, transcript, or cross-workspace history is read.

    Usage evidence reaches the report only through -ObservationPath, which takes files or directories the caller selected explicitly. A document is validated against a closed schema — schemaVersion, client, clientVersion, trust, records, and an optional coverage block at the top level and eventId, eventType, skillName, skillSha256, timestampUtc, sessionId, outcome per record — with bounded file size, record count, and field lengths, a strict UTC timestamp inside a plausible range, and a 64-character SHA-256 content identity. records must be a JSON array, so a scalar or a bare object is refused rather than silently wrapped. An unsupported property, an unsupported schema version, an unsupported event type or outcome label, or an unparsable document is refused rather than partially read, and a rejection names the field and the rule and never echoes the offending value, so no imported payload or secret can travel back out through an error. Prompt text, Skill bodies, tool responses, and free-form notes are not in the schema at all. Nothing is stored, and nothing is uploaded.

    Provenance survives the import. An event identifier is unique only inside the client and session that minted it, so records are scoped by client, client version, session, and event identifier together, and the same local identifier from two clients no longer collides. Exact copies deduplicate onto one accepted record; records sharing an identity scope that disagree on their payload are reported as a conflict listing the import locator of every variant, and none of them is accepted, because accepting one would mean accepting whichever file happened to be read first. Every accepted record keeps its source path and declaring client, and the accepted set is order-independent. Enumeration is bounded and guarded during the walk: every directory is checked before it is descended into, the file bound is enforced as files are collected rather than after the whole tree has been listed, and an explicitly selected file is guarded against its own parent directory so an ancestor link cannot redirect the read.

    The facets stay apart. An observed file read, a Skill activation, a tool execution outcome, demonstrated quality, discoverability, freshness, and description overlap are counted and reported separately, and no facet is collapsed into an invented single health score. A successful load or read is never reported as a passed capability evaluation. Trigger-query sets prove that discovery material was authored, never that it was measured; the command runs no model and adds no second evaluation engine. Every record is bound to the SHA-256 of the body it names, so records written against a different body are counted and labelled separately instead of merged into the current one, and the report exposes the deduplicated event count, the duplicate count, the conflict count, and the observation time window.

    Evaluation evidence is read in the shapes the agent-evals Skill actually defines rather than in an invented one. evals.json is authored input in either the Agent Skills shape or the bundled harness shape and proves only that cases were written. grading.json and benchmark.json are run output, and neither carries a Skill identity of its own, so a run describes the current body only when a validated sidecar named after the artifact with its extension replaced — grading.provenance.json beside grading.json — names this Skill, the SHA-256 of its current body, and a real completion instant no later than the reference instant. A run bound to a different body is labelled DifferentBody and excluded, and a run whose provenance is missing, malformed, or without a plausible completion instant stays visible as Unbound and is never counted. A run identifier is a quality identity and is consumed only by a graded result that could be counted, so a benchmark arm sharing it costs nothing; copies that agree are counted once as DuplicateRun, and copies that disagree are labelled ConflictingRun and none of them is counted. A graded run is its assertion list and the summary is a claim about it: the claim counts only when passed, failed, and total reconcile exactly with the bounded Boolean verdicts recorded in assertion_results, so a summary with no assertions, a contradicted verdict, an ungraded case, a non-Boolean verdict, and a count that is missing, negative, non-numeric, or outside the supported range are all reported and none becomes a pass. Assertion text and evidence are never read out of an artifact. Every file the report opens is size-bounded, the assertion list is count-bounded, and an oversize artifact is reported as such instead of being read.

    The supported client event contract is verified rather than asserted. Each enumerated hook event is checked against the deployed authoring Instruction under the inspected content root, the report publishes the resulting verification state and the scope of the check, and the claim is scoped to this implementation — no reliable Skill-activation contract verified for this implementation — instead of asserting that no client anywhere can identify a Skill. A frontmatter fence is likewise not treated as proof of valid metadata: the block is reported as parsed only when every line is a supported top-level key and both name and description resolve to a scalar, and anything else is reported conservatively as unsupported.

    Missing telemetry means unknown, never zero use, and an imported SkillFileRead is evidence of a read rather than of an activation. Every record is reported as Imported — a document claiming observed trust is recorded as a claim and still read as imported, because no capture this module performs exists. Suggestions are advisory: each cites local evidence and carries Decision = 'HumanReviewRequired'. A RetirementReview is never inferred from silence: it is raised only when an import declares explicitly, for one Skill and one body, that activation capture was complete over a window of at least 30 days and 20 sessions that closed within the staleness horizon, and that window recorded no activation. It is never raised for a mandatory Skill and never from a window belonging to a different Skill.

  • Add the reviewed-learning-inbox Skill: an on-demand, project-scoped review queue that turns an explicitly selected local correction into a reviewable suggestion for an existing Skill or Instruction, and never into policy on its own. A candidate carries a stable project-scoped identifier, a sanitized single-line lesson, project-relative evidence locators with their SHA-256 content identity, and separated observations, interpretations, and contradictory evidence. Equivalent lessons deduplicate onto one record and increment an occurrence count, rejections and supersessions are retained so a repeated observation cannot resurrect a decision the user already made, and the confidence field is stored with the note that it is a review label rather than a probability or permission to apply. The store lives at .memory-bank/learning-inbox/candidates.json, outside the routed Memory Bank base, outside every Skill description, and outside the deployed Customization tree, so an unreviewed entry never reaches a trusted context surface.

    Promotion is append-only, previewed, and hash-gated. New-LearningPromotionProposal.ps1 renders the exact lines a promotion would add together with the destination's current SHA-256 and writes nothing to any Customization; Invoke-LearningPromotion.ps1 refuses the change unless the caller passes -Approve with the SHA-256 of the preview that was actually read. Before the first byte is written it also refuses a proposal from another project, a destination that contradicts the candidate scope, a destination outside an existing SKILL.md or *.instructions.md or outside the project, a proposal whose evidence list differs from the authoritative candidate record, and a candidate record, evidence file, destination, or rendered preview that changed after the preview was produced. Every path is checked by walking each existing directory from the selected project root down to the file, so a junction or symbolic link on an intermediate directory cannot redirect a read, a hash, or a write outside the project — the leaf-only check that shipped first could be bypassed that way.

    The apply step opens one write-exclusive handle, hashes the bytes it is about to append to, and appends through that same handle, so an edit made between the review and the write is refused instead of overwritten and the original byte prefix — including a UTF-8 byte order mark — survives exactly. A destination that is not UTF-8 with or without a byte order mark is refused rather than silently re-encoded, and a failed verification truncates back to the original length. The store is replaced atomically under a bounded, fail-fast per-inbox mutation lock and refuses to overwrite a store that changed after it was read, so two concurrent writers cannot lose a candidate. Repeat application is bound to the block that is actually present rather than to a marker string: a block that matches the candidate record and a recorded promotion reports AlreadyPromoted, an apply that appended the block but never recorded it reports ReconciliationRequired and then Reconciled under the same human approval without touching the destination again, and a forged or edited block is refused with the recovery step named.

    Text taken from a selected artifact is treated as an untrusted observation at both ends of the pipeline. Intake and promotion apply the same rule: single-line printable prose only, so code fences, shell substitution, markup, control characters, and capability or scope keys such as tools, model, agents, and applyTo are refused, and hand-editing the store widens nothing. That allow-list is format validation and is documented as such: it is not an injection-proof or secret-redaction boundary, explicitly sanitized input and human review remain necessary, and -Approve plus a preview hash is an approval protocol rather than proof of human identity, which no automatic agent may supply without a current human instruction. Only Skill and Instruction scopes are promotable at all; NewSkill, NewInstruction, and NewAgent are suggestions for a human and require a written overlap explanation before they can even be recorded. evals/candidate-cases.json carries offline cases only, including one real local correction represented by repository locators and line ranges rather than any transcript, and is labelled authored with executed set to false because no model-backed sweep has been run for it.

  • Add opt-in installation profiles. Install-CopilotAtelier, Update-CopilotAtelier, and Setup-CopilotSettings.ps1 accept -InstallationProfile with complete (the unchanged no-argument default), engineering, research, and document-processing, plus -IncludeSkill and -ExcludeSkill for adjusting a selection by Skill identifier. Only Skills are selectable: Agents, Instructions, Prompts, and Hooks always deploy in full, and memory-bank, long-running-job-monitor, and agent-security-review stay in every selection because the deployed Instructions and shipped Custom agents load them by name. A selected Skill ships its whole folder, and the Skills it hands part of its workflow to come with it. Unknown identifiers, a Skill both included and excluded, an excluded mandatory Skill, an excluded dependency of a selected Skill, and a cyclic dependency catalog are all rejected before any directory, Discovery link, setting, or Deployment record is touched. A narrowing request is also checked against the payload it narrows: a mandatory Skill the payload does not ship, a selected Skill whose required Skill is absent, and an explicitly selected directory without a SKILL.md entry point are refused with the dependent and the absent target named, rather than resolving to an incomplete selection. The complete installation keeps deploying a payload exactly as it is and reports PrerequisiteValidated as False instead of claiming it validated one. The selection is recorded as an additive optional Selection field in the schema-1 Deployment record, whose shape is validated strictly — arrays rather than scalars, no duplicate or contradictory identifiers — while an absent Selection stays valid, so records written before profiles existed still read as the complete installation, and an argument-free reinstall or update keeps the recorded selection instead of silently re-expanding it. An inherited selection is re-read and re-resolved once the run holds the local deployment lock, so a concurrent local installer that changed it is followed rather than overwritten from a stale read. Ownership stays with the recorded Files list alone; a claimed Selection never confers it. Switching profiles retires only unchanged Owned files: user-added files stay, and a locally changed file stops the switch with its path named. Profiles apply to the module and clone paths; the native Agent Plugins channel installs the whole package and has no selection mechanism. See choosing what gets installed.

  • Add read-only Get-CopilotAtelierProfile, which resolves every installation profile against a payload and reports the Skills, mandatory Skills, prerequisite-validation status, and counts each one deploys without changing anything. Test-CopilotAtelier now reports the deployed InstallationProfile and fails health when a record excludes a mandatory Skill.

  • Add read-only Get-CopilotAtelierFootprint, an on-demand report of the customization collection's loading footprint with concrete opportunities to reduce unnecessary loading. It reuses the shared deployment directory map and recognizes actual Customization file types: only an explicit broad applyTo and Skill discovery metadata are reported as potential automatic loading, contingent on discovery and client applicability, while missing, malformed, ambiguous, or unsupported scope metadata and unselected Custom agents stay unknown applicability rather than always-loaded, and ancillary documents, scripts, and binary assets are reported as disk footprint rather than model context. Ambiguous frontmatter — an unmatched quote or a duplicate key — is rejected as unsupported rather than trusted. It enforces every mapped root against the selected content root, so a junction at the selected root, at an intermediate namespace folder, or a mapped path that escapes via traversal is reported and never followed, and it labels every byte figure as a file-size estimate rather than measured session loading, proven activation, or duplicate runtime injection.

  • Add explicit -Repair for modified Owned files and -TargetPath selection through Install, Update, and Setup, with preview support and untracked-file preservation. See repair and recovery.

  • Add read-only Test-CopilotAtelier deployment diagnostics and hash-aware Uninstall-CopilotAtelier, with explicit targets, non-interactive account resolution, and preservation of user content and configuration. See deployment diagnostics and removal.

  • Record per-file SHA-256 ownership in the Deployment record and validate paths and metadata before deployment or removal.

  • Bound SessionStart context to 4096 characters by default, configurable from 1024 through 16384 without disabling lifecycle or security hooks.

  • Gate Customization tool bounds, delegation, Prompt overrides, hook timeouts, and remote authorization with adversarial fixtures and a shrink-only MCP baseline. Extend the existing authoring guide with implementation selection and evidence-based pattern promotion.

  • A repository-scoped migration for legacy career, legal, and tax Memory Bank records (2026-09-04). The memory-bank Skill now separates planning from applying: it inventories only direct children of one selected .memory-bank/, classifies known legacy names, requires explicit decisions for ambiguous files, saves a metadata-only plan, and previews with -WhatIf. Apply validates the complete plan before writing, rejects changed sources, conflicts, path escapes, cross-repository plans, and reparse points, then performs byte-exact, SHA-256-verified copies without overwriting or deleting any source. Career Coach, Legal Researcher, and Tax Researcher invoke this workflow before creating empty namespaced replacements.

  • Interactive browser access for every web-capable Custom agent (2026-09-04). Replace the remaining preview-only openSimpleBrowser entries with VS Code's built-in browser tool while keeping the Contoso profile browser-free.

  • Semantic Custom agent contract tests (2026-09-04). Validate every handoff target, required delegation surface, DevOps composition contract, role-specific Memory Bank namespace, browser tool, cross-client README caveat, and the 30,000-character prompt limit with a shrink-only baseline for the four existing oversized agents.

  • Interactive web-application troubleshooting in the Software Engineer agent (2026-09-04). Replace the preview-only openSimpleBrowser entry with VS Code's built-in browser tool set so the agent can navigate and exercise its product, inspect page content, console errors, and screenshots, verify affected desktop and mobile viewports, fix defects, and repeat the original flow. Browser checks default to agent-opened ephemeral sessions on loopback origins; authenticated state is available only when the user explicitly shares a tab. Session evidence complements rather than replaces repository regression tests, and the Contoso overlay continues to omit browser access.

  • A Prompt-led specification completion workflow for any spec-driven repository (2026-09-02). /complete-specifications inventories acceptance criteria, milestone exits, Decision gates, local gaps, test evidence, and an optional local issue snapshot into a frozen closure matrix. A restricted controller creates one isolated work item per missing behavior, dispatches one test-first implementer for each, and sends every result to a fresh read-only reviewer before integration.

    Four percentages prevent "implemented" from silently meaning "proven": implementation, passing unit and integration tests, live verification, and total specification closure each keep their own numerator and denominator. Every non-duplicate engineering row stays in the primary denominator. Live validation defaults to off; enabling it requires a direct containment-profile digest and a hash-pinned, data-isolated live runner. A pinned append-only appender hash-chains review and accounting records outside repository processes' writable roots, and changed build commands cannot run until their control review passes. Shared and production mutation is prepared as a supervised procedure and never counted as live evidence. The package has no web, issue-mutation, or push path, caps work items, Custom agent calls, concurrency, fix rounds, and run time, and leaves every branch local.

  • A session clock, so Post-flight closes with the chat's measured elapsed duration (2026-09-02). The user asked for two more facts at the end of the checklist: when the turn closed, and how long the whole chat had run. The first half already existed — com.github.copilot/hooks/scripts/Add-SessionContext.ps1 injects Session started at <UTC> and Pre-flight opens every reply with it — which made this look like a formatting change.

    It is not, because a model has no clock. The opening timestamp is right only because a hook measured it; a closing one composed by the model would drift by the length of the turn, which is the very quantity being reported, and after a compaction the model no longer knows when the session began. The obvious fix is unavailable: VS Code's UserPromptSubmit supports the common output format only, with no additionalContext field — the same limitation already documented for PreCompact. The events that can inject context are SessionStart, which fires once, and PreToolUse/PostToolUse, which would spend tokens on every tool call of every turn and fold a timing concern into the security guardrail.

    So the number is measured on disk and read back by the one party that can print it inside the reply. Add-SessionContext now also writes the session start to <LocalApplicationData>/CopilotAtelier/sessions/session-<key>.json — on disk, so it survives compaction — and hands the agent the absolute path of a new reader, com.github.copilot/hooks/scripts/Get-SessionElapsed.ps1. The agent runs that reader as the last action of the turn and copies its single line verbatim into the checklist: POST-FLIGHT elapsed: 16m (started 09:15 UTC, measured 09:31 UTC, turn 3). com.github.copilot/rules/postflight.instructions.md gains a Session clock section forbidding the model from composing, reformatting, or recomputing either number, and telling it to report the duration as unavailable rather than estimate one when the reader is gone.

    The first attempt printed the line from the Stop hook, and was wrong in a way only a screenshot revealed: VS Code renders a hook systemMessage as a detached, collapsed Warning from Stop hook box, not as part of the reply. The number was therefore beside the checklist rather than in it, and the user asked again. A hook cannot write inside the model's output and the model cannot read a clock, so the shipped split is the only arrangement that satisfies both. com.github.copilot/hooks/scripts/Write-SessionClose.ps1 stays, because the turn counter still has to advance somewhere, but it now reports nothing unless the clock is unreadable — the one case where the agent's own line could not be measured either. Reporting the duration there as well would only have put a second copy in the warning box on every turn.

    Stop fires once per turn instead of once per tool call and costs no tokens. It emits no decision field: blocking a Stop restarts the agent and bills another turn, which is far more than a timestamp is worth. The clock avoids the temp directory because /tmp is world-writable on Linux, where a predictable name invites another local account to pre-create the path, and avoids .memory-bank/ because — unlike a compaction checkpoint — it is machinery rather than knowledge an agent reads, and it has to work in a workspace with no Memory Bank at all. The payload's session_id becomes a path component, so it is stripped to [A-Za-z0-9._-] and capped at 64 characters, with a hash of the working directory as the fallback. The reader is read-only — Stop owns turns, so it reports the turn in progress as one past the closed count — and given no explicit path it picks the newest clock recorded for the current workspace, so a second VS Code window on another folder is never measured here.

    Deploying the reader exposed a defect the suite had been creating all along. Six Add-SessionContext tests invoked the hook without -ClockRoot, so every run left real session clocks in the caller's own %LOCALAPPDATA%\CopilotAtelier\sessions — around fifteen of them, including one whose recorded workspace was C:\demo IGNORE PREVIOUS INSTRUCTIONS, written by the prompt-injection test. That was invisible while only the Stop hook read the clock, because it looks its own session up by id. The reader searches by workspace, so a clock the tests had written for this repository immediately shadowed the live session and reported a three-minute chat that had been running for an hour and three quarters. Every SessionStart invocation now goes through a helper that pins the clock root to TestDrive, a test asserts the real profile directory gains nothing, and the reader prefers a session-<id> clock over a session-cwd-<hash> fallback — VS Code always supplies a session id, so the hashed name in practice means a test or a non-VS-Code caller.

    The duration formatter shipped with a bug the tests caught: [int]1.5 rounds in PowerShell, so a 90-minute chat reported 2h 30m. It floors explicitly now, and tests/Hooks.Tests.ps1 pins five durations that sit where rounding and truncation disagree, alongside the injected reader path, the single-line output contract, the turn-in-progress arithmetic, the workspace preference, the shadowing regression, the clock-root containment, the read-only guarantee, the turn counter, the stop_hook_active continuation case, a corrupt clock, an unreadable payload, the absent decision field, and a session_id of ../../pwned.

Changed

  • Extend the Sampler Skills with version-scoped wiki commit-timeout diagnosis, supported publication-runner rationale, and destination-by-destination recovery after partial publication; retain the real incident as a transcript-graded regression case without claiming behavioral improvement from structural checks or completed requests that did not load the Skills.
  • Reject a source tree that overlaps the Canonical target. A clone kept at ~/OneDrive/CopilotAtelier/, the location earlier documentation suggested, now fails before any write; move it aside — for example to ~/OneDrive/CopilotAtelier-src/ — and reinstall. See repository clone.

Fixed

  • Fix Windows OneDrive detection so a generic OneDrive variable or pre-created folder does not select a sync target without account-specific configuration; preserve macOS/Linux discovery and explicit -TargetPath selection. See target selection.

  • Preserve the hidden client-adapter ownership manifest in GitHub Actions build artifacts so downstream jobs can verify generated files (CI run #75).

  • Use canonical temporary directories in plan-review filesystem tests on Windows and macOS, with a linked-directory regression, without weakening containment or link rejection (CI run #75).

  • Require the plan-review heading verifier at every document read, and fail the repository gate when a read stops being verified, so the check that keeps a comment anchored to the text the reader actually saw cannot regress unnoticed. An omitted verifier now raises instead of reading the document unchecked. Refuse a request body that nests deeper than the walk bound rather than leaving its deepest keys uninspected.

  • Align local plan-review section anchors with rendered indented ATX and setext headings, preserve literal trailing hashes, and refuse unsupported structures before feedback writes, including queued mutations. Report startup failures and verdict actions without a loaded revision cleanly, and exercise mutation guard composition with behavioral regressions. See the plan-review guide.

  • Validate payload entries through the shared path guard so Windows Cloud Files placeholders are accepted while redirecting links remain rejected (CI run #72).

  • Fix uninstall failures on dangling Linux and macOS Discovery links and preserve literal POSIX filenames in validation fixtures (CI run #72).

  • Fail deployment health for modified hook files and redirected event commands, including platform overrides and -Quiet; keep ordinary modified-file warnings distinct.

  • Recover interrupted file applies through atomic replacements and per-file Deployment-record checkpoints, including retries with different or older payloads; coordinate local install/removal without claiming a filesystem or cloud-sync transaction.

  • Validate portable path segments consistently before planning and when reading records, reject . and .. explicitly, use target-native filename identity, and diagnose retained capitalized legacy trees without removing them.

  • Restore result-serialization type data after successful and failed builds, parse hook JSON with ConvertFrom-Json, and check built-in and implicit Prompt tool overrides without claiming runtime containment.

  • Preserve user-added files and legacy trees during reinstall; reject locally modified files, reparse-point paths, and intervening changes instead of rebuilding deployment directories destructively. Reconcile wanted edits before reinstalling or use explicit -Repair for recorded files; -Force does not overwrite them.

  • Quote the usage Prompt argument hint so its colon parses as YAML; validate every Prompt with a full YAML parser in the configuration security gate.

  • Pin Pester 5.7.1 and reject unsupported major versions in QA. Bound filesystem references in saved test reports to paths and labels so result export does not traverse live provider and assembly metadata; retain all test counts, failures, and coverage.

  • Repair invalid Custom agent orchestration contracts (2026-09-04). Give Security Reviewer and Technical Writer an executable research-analyst delegation path, replace the DevOps writer's fictitious inheritance with explicit composition, and remove the Research Analyst pseudo-handoff that had no target Custom agent.

  • A twenty-minute Pester suite ran five times with long-running-job-monitor unloaded, because nothing in context said it existed (2026-09-04). In C:\git\RdsFarmManager an agent ran ./test.ps1 five times and monitored none of them. The cause is a loading mechanism, not a wording problem: Instructions auto-apply by applyTo glob, Skills load only when the model matches their description against the conversation, and powershell-execution-safety.instructions.md was in context the whole time while the Skill was not. The pointer between them did exist — twice — but never where it would have changed anything: once as "or apply long-running-job-monitor when ongoing progress reporting is required", a judgement call attached to the anti-polling bullet, and once under Indefinite processes, a section an agent has already left because a test run is not a daemon.

    The nuance the earlier 2026-09-01 fix missed is that the run was agent-initiated. The user asked for a code change, never for a test run; "live test", "is it stuck", and "keep me posted" were never typed, so there was no user phrasing for a description to match. Description matching cannot fire on the agent's own decision to start a long command, which makes the auto-loaded Instruction the only reliable carrier.

    The Instruction now leads with the launch decision instead of burying it. Running Tests & Builds becomes Long-Running Commands — Detach AND Monitor, Never Direct Execution and opens with a trigger that needs no judgement — anything expected to run past roughly two minutes, and unconditionally Invoke-Pester, Invoke-Build, build.ps1, test.ps1, any other test or build entry point, any installer, and any deployment entry point — stated to fire on an agent-initiated run with no user request. Detaching and monitoring are stated as one obligation rather than two that can be satisfied separately, because a correctly detached run with no START line, no phase lines, no terminal marker, and no per-turn status line was exactly what happened. The four anti-patterns the session produced are named in two lines each and nowhere else: Select-Object -Last buffers the whole stream so every progress check returns the same frozen snapshot; editing source during a verification run scores a stale artifact because build output and Pester discovery are fixed at launch; Tee-Object overwrites content while NTFS keeps CreationTime, so file metadata is not elapsed time; and a script running inside the terminal's own pwsh never appears in a process command line, so liveness cannot be guessed from one. The detach rule itself is unchanged — it was already correct and was already ignored.

    The Skill keeps the workflow and gains the vocabulary that was actually in play — "run the test suite", "full suite", "Pester run", "Invoke-Pester", "build.ps1", "test.ps1", "verification run", "regression run" — plus "a run the agent starts itself with no user request" in the summary, at 989 characters against the 1000-character soft cap. DO NOT USE FOR gains sampler-build-debug and pester-patterns so the new build-and-test terms cannot buy positives by over-triggering.

    Measured rather than asserted. trigger-queries.long-running-job-monitor.json holds twelve labelled cases taken from the session itself, and the Skill leaves the uncovered baseline in tests/SkillTriggerCoverage.Tests.ps1. Paired arms against the same 47-skill catalogue with claude-haiku-4.5 judging in a fresh context per call: train 5/7 → 6/7, validation 4/5 → 4/5, false positives 0 in both. "Run the full test suite" went 0.33 → 1.00 and the mid-flight unrelated question 0.00 → 0.33. The agent-initiated case stayed at 0/3 in both arms, which is the point rather than a shortfall — it is the measurement that says the Instruction, not the description, has to carry that path. Full result and caveats in notes-evals.md E11.

  • Make remote-mutation hooks resolve deterministically and fail closed (2026-09-02). Hook commands now use only the exact PLUGIN_ROOT or ~/.copilot/hooks/scripts path, never a version wildcard, and exit 2 with a diagnostic when the security script does not resolve. Missing lifecycle scripts warn without blocking the agent loop. The command matcher recognizes git.exe, fully qualified Git executable paths, and GitHub CLI global repository or hostname options before mutating commands, closing ordinary bypasses while preserving read-only and local commands.

  • A 45-minute live proof ran with long-running-job-monitor unloaded, and the chat stayed silent for thirty minutes (2026-09-01). An agent launched a live Hyper-V proof in the Vivarium workspace, hand-rolled Start-Process plus WaitForExit instead of the canonical detached launcher, armed no cadence tick, and answered two mid-job turns with no status line. The user had to ask "are you running a task in the background?" and then "didn't we update the skill so the user gets a status update every n minutes?" — a Skill that was never read cannot be followed, so this is three defects in skills/long-running-job-monitor/SKILL.md, not one.

    The first is a vocabulary gap in the description, which is the only thing the selector sees. Vivarium's glossary makes proof the canonical term for a live integration run, and the USE FOR: list carried "live test" and "integration test" but not the word the domain actually uses. It now names live proof, proof harness, proof run, and hour-long run; the description stays at 961 characters, under the 1000-character soft cap. The second is a typo in the same list — "log log tail" is now "log tail". Both are one-line fixes that only matter because a description this skill never triggers on is a description that does nothing.

    The third is structural. Arming the cadence tick was described in the Chat heartbeat section and in a checklist item prefixed "For unattended cadence", so nothing on the launch path itself required it — an agent could follow step 2 to the letter, detach the job correctly, and end the turn with no tick armed. Step 2 now carries the imperative directly: arm in the same turn as the launch, before the turn ends, whenever the job is expected to outrun the cadence interval, and a detached launch with no armed tick is named as the exact failure the Skill exists to prevent. The checklist item is unconditional. notes-evals.md gains E10, a trigger-rate eval whose prompt is a live-proof launch in Vivarium's vocabulary that never says "monitor", "heartbeat", or "background", so the Skill has to be selected on the description alone.

  • The mandatory disclaimer travelled into two signed submissions to a German tax office (2026-08-31). com.github.copilot/agents/tax-researcher.agent.md opened with "include this at the end of every substantive output", and the model did exactly that: an RDG and StBerG notice ended up below the signature block of two Einspruchsbegründungen, where skills/german-tax-research/SKILL.md had forbidden it since the Skill was written. The defect surfaced only when the taxpayer had already printed and signed both letters.

    The two rules were both present and contradicted each other. The agent's instruction was unqualified; the Skill's fourth non-negotiable said submissions carry no internal caveats. An unqualified instruction in the agent body beats a rule three sections into a Skill, so the agent is where the fix belongs: the disclaimer now applies to chat replies and internal working papers, and never to a Schriftsatz, Einspruch, Anlage, Eigenbeleg, or Erklärung that a taxpayer signs. The reason is spelled out rather than asserted — in a letter the taxpayer signs, a notice disclaiming tax advice reads as if an unauthorised third party had drafted it.

    A rule nobody checks is a rule that fails silently, so both files now carry the check. The agent gains a marker sweep in phase 5 and an anti-pattern for shipping a Schriftsatz PDF without one. The Skill's non-negotiable 4 names the production failure, lists the search terms — StBerG, RDG, Steuerberatung, intern, Entwurf, Prüfvermerk, TODO — and requires the sweep twice: once against the Markdown and once against the rendered PDF's text layer, because a template or a CSS rule can reintroduce what the source no longer shows. The existing Marker sweep verification item is extended accordingly.

Changed

  • Separate career, legal, and tax records into role-specific Memory Bank namespaces (2026-09-04). Use .memory-bank/career/, .memory-bank/legal/, and .memory-bank/tax/; preserve ambiguous legacy files until the user explicitly assigns and verifies them.

  • Document cross-client Custom agent differences and staged sensitive-data research (2026-09-04). Clarify that plugin discovery does not guarantee identical model or tool behavior, and preserve local file, OCR, web, and authenticated-browser workflows by separating private intake, local transformation, minimized public research, and user-confirmed actions.

  • german-tax-research gains a disclosure economy (2026-08-31). A Begründung addressed to a tax office had been disclosing which receipts were missing for positions nobody had questioned, explaining at length why items were not claimed, and conceding reductions the office had not proposed. Each sentence was true; together they handed the examiner a worklist he had not written.

    The new section separates two duties that get conflated. § 150 Abs. 2 AO requires the declared bases of taxation to be complete and true; it does not require a self-assessment of how strong the evidence behind them is. §§ 90, 97 AO oblige cooperation and production — on request, and under the Belegvorhaltepflicht that request often never comes. One test decides every sentence: does it support an amount that is actually declared?

    Estimates, deviations from the transmitted return, method changes, § 153 AO corrections, and positions maintained against a contrary document must still be disclosed — silence there is the real risk. What must not be volunteered is the evidentiary weakness of a claimed and consistent position, any reasoning for a position that is not claimed at all, the fact that a figure rests on the taxpayer's own statement where no third-party document could exist, speculation drawn from a bank entry, anticipatory concessions, and promises of documents nobody asked for. Three exceptions keep a non-claimed item in the letter: a cross-year inconsistency the office would otherwise spot, a double-deduction reproach worth forestalling, and a correction against the taxpayer. Two anti-rationalizations, three red flags, a Disclosure sweep verification item, and an anti-pattern make it checkable; the tax-researcher agent gains the matching phase-5 probe and four German anti-patterns.

Changed

  • The Software Engineer agent no longer hands work to security-reviewer on its own judgement (2026-08-28). com.github.copilot/agents/software-engineer.agent.md gains an explicit independent review switch that is off by default, so a routine change now ends with the agent's own validation and self-review instead of a subagent dispatch that costs minutes of latency per turn.

    The old rule read "request an independent review with a subagent for high-risk work" and then listed security or identity boundaries, destructive operations, persistence, concurrency, public APIs, cross-module contracts, and "a large unfamiliar diff". In an agent-customization repository almost every change matches at least one of those, and the Design and security rule pointing at agent-security-review for "agents, LLM-backed features, RAG, or MCP servers" matches the rest — so the risk-scaled default behaved as an unconditional handover. The trigger list survives unchanged; what changed is what it triggers.

    The switch is user-set, not model-set: review: on requests one independent review of the finished change, review: auto restores the previous risk-scaled dispatch, and review: off is the default. Plain language and the existing Run Security Review handoff button both count as on, so the fast path stays available without new syntax to learn. argument-hint advertises it in the picker.

    Turning the default off without losing the signal needed one more piece. With the switch off the agent still evaluates the same risk list, but it names the risk instead of reviewing it: the work finishes and the closing line recommends review: on and states why. com.github.copilot/rules/postflight.instructions.md gains the matching clause, because the shared Definition of Done gate demanded that independent review "was completed" — a contradiction the model would otherwise have resolved by dispatching anyway. The requirement stands for every other agent; only the deferral path is now named.

    com.github.copilot/agents/software-engineer-contoso.agent.md is unaffected by design. Its inlined base body is re-synced, but the overlay pins the switch to on for security-relevant diffs, new dependencies, new network paths, and first-time repositories, and states that a review: off request downgrades nothing there — an overlay that only adds constraints must not inherit a relaxation. tests/SoftwareEngineerAgent.Tests.ps1 covers the default, the three switch values, and the argument-hint, so the auto-handover cannot come back silently.

Added

  • elster-form-capture, a Skill for driving the Mein ELSTER web form by machine (2026-08-31). Three full capture runs across two assessment years produced the material: skills/elster-form-capture/SKILL.md fills a German income tax return field by field while the taxpayer signs in, reviews, and presses Send.

    The boundary is legal, not technical. Transmission is the taxpayer's declaration of knowledge under § 150 Abs. 2 S. 1 AO, so filling fields is assistance and sending is not delegable — Versenden des Formulars is a non-negotiable the Skill never presses. It handles no credentials either: the user authenticates and shares the page.

    One fact carries the whole Skill. The official ERiC field numbers behind name="fields[…]" are stable across assessment years; the Teilseite and Zeile numbers are not. The Anlage V was reorganised for 2023 and renumbered again for 2024 — apportioned costs moved from sub-page 12 to 13, the result and allocation from 17 to 18, and sub-letting left the attachment entirely for a new Anlage V-Sonstige — while not one data-eru-name changed. So the Skill addresses fields by Kennzahl, verifies by sub-page heading, and treats a line number from a guide written for another year as a claim to be checked. references/feldkarte-est.md carries the harvested numbers for Anlage V, V-Sonstige, N, and Vorsorgeaufwand, with the 2023-to-2024 movements tabulated above them.

    The 29 gotchas are corrections, not advice; each one cost a failed attempt. The three that generalise beyond ELSTER: a page.goto() discards a select box the server has not yet acknowledged, because the beforeunload dialog takes the change with it — three running numbers were set in a loop and only the last survived. The add button of a sub-form shares its id prefix with edit, differing only in a trailing index, so .first() silently overwrote the first foreign country with the second. And a value transferred as eData can itself be the error: an employer reported 0.00 € for statutory health insurance, and the field had to be emptied rather than left at zero.

    The finding that justifies the whole approach is not the typing. Driving the form mechanically turned out to be the fastest audit of the capture guide that feeds it — it caught a wrong postcode (the insolvent developer's address, not the property's), a stale line reference, and, through a machine comparison of 52 target amounts against the summary page, 1,330 € of deductions that a status table already recorded as captured. Hence the rule that the final check is a comparison and never a reading: values produced by someone else are demonstrably reviewed less carefully than values one typed.

    skills/agent-evals/assets/trigger-queries.elster-form-capture.json carries ten positives in both German and English and ten near-miss negatives drawn from the neighbours the Skill must not displace: substantive deductibility and Einspruch drafting belong to german-tax-research, login persistence to authenticated-web-extraction, receipt reading to pdf-to-markdown and xlsx-to-markdown, the Anlagen bundle to evidence-package-assembly. "Fill in this PDF form" is included deliberately as the closest false friend.

  • An opt-in four-stage development cycle across the agents (2026-08-28). cycle: full runs architect → engineer → security reviewer → technical writer as one requested workflow, with the reviewer as the gate: on pass it hands to the writer, on fail it hands back to the engineer. It is off by default and starts only because the user asked for it — never because the work looked like it deserved one.

    The distinction that makes this safe is the one the same release draws for review: on: consent at the entry point covers the whole chain. What the previous entry removed was delegation the agent chose; automatic progression inside a cycle the user requested is the opposite thing, so the stages flow without further clicks once the switch is set.

    Two problems had to be solved before the chain could work at all. The first is close-out: the shared Post-flight makes every substantive turn write the Memory Bank, add a changelog entry, and commit, so four stages would have produced four of each for one change. com.github.copilot/rules/postflight.instructions.md now defers those steps to the final stage, and the three earlier stages verify their own work, refresh activeContext.md, and hand over. The second is the failure path — reviewer → engineer → reviewer is a loop, not an arrow.

    The first attempt at bounding that loop was a prose cap: "after two rounds, stop the cycle and report the unresolved findings". It could never fire. A handoff starts the receiving agent with fresh context, so neither side can see, let alone count, the rounds it has already run — the cap was an instruction with no state behind it. Paired with two handoffs that both auto-submitted, it produced a run of fifteen complete software-engineersecurity-reviewer round trips that only stopped when the session was abandoned. The bound that ships instead is structural: the reviewer's Fix Issues Found handoff sets send: false, so the cycle runs forward on its own but re-entering implementation costs one deliberate click. A ring of auto-submitting handoffs is now a test failure rather than a judgement call.

    State passes on disk, not through the conversation. The architect writes the signed-off Design Concept to .memory-bank/decisions/ and the engineer reads it from there, because a conversation does not survive a compaction and a subagent never sees one to begin with.

    Every forward handoff in the cycle sets send: true, so a transition submits on selection instead of populating the box and waiting for a second confirmation — inside a cycle the user already consented at the entry point, and asking again is the ceremony the switch exists to remove. The reviewer's fail path back to the engineer and every non-cycle handoff keep send: false. Progression is still surfaced as a handoff rather than an unattended agent switch, which is a platform boundary rather than a design choice: VS Code hands the user to another agent, an agent cannot hand itself.

    Nobody speaks in switches, so software-architect carries a phrase book — "full development cycle", "full workflow", "development cycle", "full SDLC", "full pipeline", "the whole pipeline", "the full agent chain", "all four agents", "design to documentation", "concept to docs", "run the complete workflow" — and, more usefully, a refusal list. "end-to-end" normally means end-to-end tests, and "do it properly", "the whole thing", and "ship it" name nothing; a four-agent cycle is too expensive to start on a guess, so those prompt a question instead. A cycle requested at the engineer rather than the architect hands back upstream, because starting in the middle means there is no signed-off concept to implement.

    The rules live in the four agent bodies rather than in a Skill, and that is deliberate: this repository already established that a Skill is advisory content while an agent body is mode instruction, which is why grill-me had to become the software-architect agent. Close-out ownership has to bind, so it is stated where it binds — but the loop bound is deliberately not prose, because prose is exactly what failed. The only new frontmatter edge is security-reviewertechnical-writer, which is what closed the graph; every other leg already existed. tests/DevelopmentCycle.Tests.ps1 asserts the stage declarations, the connected chain, the gated fail path, the trigger and refusal vocabulary, the auto-submitting forward handoffs, the escape hatch, that exactly one stage claims close-out, and — by walking the whole handoff graph — that no ring of send: true edges exists in any agent.

    cycle: off ends a running cycle at whichever stage holds the work, and that stage becomes the closer rather than leaving the changelog entry and the commit stranded on a chain nobody will finish. The reviewer additionally has to name any unresolved Blocker on the way out, because a stopped cycle is the easiest place for one to disappear.

    Both switches are documented where a user will look rather than only in the agent bodies: README.md carries a five-row table from "nothing" to cycle: full, and com.github.copilot/agents/README.md expands it with what each setting actually does. The table leads with the default — doing nothing keeps the work with one agent — because that is the question the previous entry left unanswered. The release-pipeline diagram there gained the technical writer stage it had been missing and the gated fail edge.

  • software-engineer-contoso — a corporate overlay on the Software Engineer agent (2026-08-27). com.github.copilot/agents/software-engineer-contoso.agent.md carries the whole software-engineer.agent.md contract inline and then only tightens it: where the overlay is stricter it wins, where the base is silent the overlay governs, and where both are silent the stricter reading applies. It exists both as a usable agent for regulated work and as the copy-and-rename template for software-engineer-<company>.

    Inheritance is by inlining, not linking, and the first attempt proved why. Modelled on devops-training-writer — which states its inheritance from training-writer in prose — the overlay originally opened with a Markdown link to its base and the sentence "read it as part of your operating instructions". Nothing was inherited. VS Code resolves referenced instructions files into the prompt, which is what chat.includeReferencedInstructions governs and what the documentation means by "reference other files by using Markdown links, for example to reuse instructions files"; an .agent.md is not an instructions file, so the link is inert and the overlay ran as a bare fragment with the base contract missing. The setting being enabled is not the fix, and the failure is silent — the agent loads, answers, and simply has none of the engineering rules its own text claims to apply.

    Inlining is also the correct design here rather than a workaround, and the agent's own doctrine is the argument: a rule the model can route around is not a control. An overlay whose base contract depends on the model choosing to open a second file has exactly that weakness, in the one agent least able to afford it. One file now holds the complete envelope, which is also what an auditor needs in a regulated environment. The cost is a duplicate that can drift, so tests/AgentInheritance.Tests.ps1 compares the inlined block byte-for-byte against the base body — dropping its H1 and demoting its H2s, the one documented transformation — and fails the moment the base moves. Line endings are normalised before the comparison because git rewrites them on checkout; without that the test fails on encoding rather than content, which it did on the first run.

    The containment is in the frontmatter, not only in the prose. The base agent's 45 tools drop to 36: web/fetch, web/githubRepo, web/githubTextSearch, openSimpleBrowser, github, useMcp, vscode/installExtension, vscode/extensions, and codeInterpreter are removed, so private-data access and untrusted content cannot combine into the lethal trifecta because the third leg is gone. Prose then closes the three ways an agent reconstructs a removed capability: the terminal (curl, Invoke-WebRequest, ssh, a public-registry install), the user ("switch agents and paste it for me"), and a handoff — which is worth naming explicitly, because a handoff moves the user into another agent's toolset and stops this file binding at that moment.

    The subagent rule is the one that is easy to get wrong. agents is narrowed to security-reviewer, and technical-writer is dropped — but security-reviewer itself holds web/fetch, github, and useMcp, so delegating to it re-opens the channel the toolset just closed. Removing it was not an option, since the same overlay makes its review mandatory rather than risk-scaled for security-relevant diffs, new dependencies, new network paths, and first-time repositories. The rule that ships instead constrains the dispatch: hand it repository paths, symbol names, and the question, never pasted source, configuration values, hostnames, or data samples, and write every dispatch prompt as if it will leave the boundary.

    The rest is the control set a regulated employer actually imposes: secrets by reference from the vault with a discovered credential treated as burned (rotate, then scrub — never silently deleted, which hides a leak without revoking it); internal-mirror-only dependencies that need a pinned version, integrity verification, an approved license, and an SBOM entry, all four or none; a "never ship" Blocker list covering hand-rolled crypto, disabled TLS verification, dynamic execution, injection-prone concatenation, wildcard authorization, swallowed security failures, sensitive logging, and — the one usually left implicit — weakening an existing control as a side effect of a feature; synthetic-only test data; separation of duties that ends the agent's entitlements at the local working tree; and a seven-item hard stop whose escalation report names what state was left behind and who must act.

    The Memory Bank extension adds contoso-controls.md and data-classification.md and explicitly refuses threat-model.md, assessment-log.md, and security-playbooks.md, which security-reviewer owns — the house rule that an agent does not write another agent's role files is only enforceable if each overlay states which files are not its own. tests/SharedLifecycle.Tests.ps1 carries the per-agent baseline so the toolset, the handoff targets, and the Memory Bank section cannot drift without a test naming the change.

    Inlining has one consequence worth stating outright: the overlay contradicts itself by construction. The inherited block says in its own words that review: off is the default and that independent review is the engineer's call, while the overlay pins it to review: on and refuses the downgrade. A precedence clause resolves that — but only for a reader who reaches it, roughly 180 lines later. The reversed defaults are therefore named in the preamble before the inlined block, so the correction arrives ahead of the contradiction rather than after it, and tests/AgentInheritance.Tests.ps1 asserts that ordering rather than merely the presence of the precedence language.

Fixed

  • Hook commands no longer carry a $ token for the host to eat (2026-08-27). Every hook died at startup with An expression was expected after '(', and the only trace was a Warning from Session Start hook balloon — so the never-push block, the Memory Bank probe, and the compaction checkpoint were all silently absent while looking installed. The cause is that the host substitutes $ tokens in the command string before the child process parses it. "$b = if ($env:PLUGIN_ROOT) { ... } else { Join-Path $env:USERPROFILE '.copilot\hooks' }" reached PowerShell as " = if () { ... } else { Join-Path C:\Users\install '.copilot\hooks' }": $env:USERPROFILE was resolved by the wrong layer, and $b, $env:PLUGIN_ROOT, and $LASTEXITCODE were resolved to nothing at all. The previous design assumed the opposite — that VS Code spawns the command with no shell, so each command had to expand its own path — and com.github.copilot/hooks/README.md said so in as many words.

    The commands in com.github.copilot/hooks/hooks.json are now written without a single $, which makes them correct under both readings rather than betting on either. Paths come from [Environment]::GetEnvironmentVariable('USERPROFILE') instead of $env:USERPROFILE, and the blocking exit code — which -Command otherwise flattens to 1 and would have turned a hard block into a warning — comes from Get-Variable -Name LASTEXITCODE -ValueOnly. Each candidate path is built with [IO.Path]::Combine('/', <root>, <relative>) so that an unset root yields a drive-rooted path rather than a workspace-relative one; without the leading /, opening an untrusted repository that happened to contain com.github.copilot/hooks/scripts/ would have executed its scripts on every tool call.

    Script resolution also gained the deployment path it was missing. A plugin install materialises at ~/.vscode*/agent-plugins/<host>/<owner>/CopilotAtelier/, which is not PLUGIN_ROOT unless the client chooses to set it — a variable this repository adopted on inference and never confirmed. Each command now probes PLUGIN_ROOT, then the module's ~/.copilot/hooks, then the plugin location, and runs the first script that exists, so both supported installs are covered whether or not the client cooperates.

    tests/Hooks.Tests.ps1 grew the guard the old suite could not have had: it models the host's substitution pass over the shipped command, asserts the string survives it unchanged, and then spawns the substituted result and requires exit 2. The static "no $" assertion alone would have caught this regression; the executable half is what proves the replacement actually blocks. The first attempt at that test wrapped the command in a second PowerShell and reported 1 instead of 2 — an artifact of the wrapper's own exit-code translation, not of the hook — which is itself the reason the substitution model is the one that shipped.

Changed

  • Narrowed the role-file clause in the Pre-flight Instruction so its scope cannot be read backwards (2026-08-27). Step 3 of com.github.copilot/rules/preflight.instructions.md closed on "Create only the active Custom agent's required role files", which parses most naturally as the files that role requires — the agent's entire declared list — and that is the opposite of the intended rule. The authority is skills/memory-bank/SKILL.md initialization step 6: an agent's role list is an additive schema, and only the files the current durable workflow actually needs get created. The clause now says exactly that, and adds the negative case the old wording never carried — never scaffold another agent's schema, and never pre-create a declared file this task does not use. The failure mode it prevents is not cosmetic: an eagerly created role file is an empty template that a later turn routes to, reads, and treats as authoritative project knowledge. Both agents that declare a role schema already agreed with the corrected reading, so this aligns the shared contract with them rather than changing behaviour anywhere else.

  • Migrated the plugin package to Agent Plugins 1.0 (2026-08-26). plugin.json now declares "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", which is the field VS Code and the GitHub Copilot CLI use to select the format. The 1.0 manifest schema is closed and its component locations are fixed: skills are discovered from a lowercase ./skills at the package root and nowhere else, and the agents and skills path fields the legacy Copilot format honoured are no longer manifest fields at all. Declaring the schema without moving anything would therefore not have failed loudly — an unknown top-level field is reported and ignored, so the package would have kept loading while silently contributing zero skills. That trap was already written down as a guard in tests/PluginManifest.Tests.ps1, which refused to let the schema be declared while the folder was still Skills/; this change satisfies the guard rather than deleting it.

    The repository layout moved to match the format, and the package is now the primary layout rather than a second view of the module payload. Skills/ became skills/ — the portable component location, and the one rename that also fixes a latent bug, because the deployed folder is ~/.copilot/skills and every case-only mismatch was already broken on case-sensitive filesystems. Everything Copilot-specific moved into the client-extension namespace under the names the format gives it: Agents/com.github.copilot/agents/, Instructions/com.github.copilot/rules/, Prompts/com.github.copilot/commands/, and Hooks/com.github.copilot/hooks/ with the mandated hooks.json filename. A client that does not own that namespace ignores it without rejecting the package, so the portable half stays portable. Hooks are a clear gain: the legacy manifest never carried them, so a plugin install previously had no guardrails at all — the never-push block and the Memory Bank probe reached module users only.

    The rules/ and commands/ file formats are not documented by VS Code or the Copilot CLI, so whether .instructions.md and .prompt.md register from a plugin install is unverified. Moving them anyway is a one-sided bet: if a client rejects them they simply do not load from the plugin, while the module path keeps delivering them to the same deployed locations. There is no state in which it is worse than leaving them at the root, and one in which it is better.

    Hook commands had to learn two roots. A plugin is installed outside the workspace, so a relative path cannot work, but the same file also ships to ~/.copilot/hooks through the module — so each command now resolves $env:PLUGIN_ROOT when a plugin host sets it and falls back to the user profile otherwise. Both branches are asserted, and the existing spawn-without-a-shell test clears PLUGIN_ROOT explicitly so the user-profile branch is genuinely exercised rather than accidentally taken.

    The deployment contract is preserved by translation rather than by layout. Install-CopilotAtelier still produces ~/.copilot/{agents,instructions,skills,prompts,hooks}; because the source and deployed layouts are now deliberately different shapes, the installer carries an explicit deployed-name → source-path map — com.github.copilot/rules deploys as instructions, com.github.copilot/commands as prompts. Canonical target directories are lowercase to match their discovery links, and because a case-insensitive filesystem quietly reuses the old directory while a case-sensitive one keeps a second copy forever, the installer sweeps the capitalised names from a previous release using a case-sensitive comparison. Renaming the deployed directories to match the namespace was rejected: it would require pinning chat.instructionsFilesLocations, and a chat.*FilesLocations setting replaces the default location map rather than extending it — the same trap that silently disabled every hook location once already.

    One consequence is recorded rather than hidden. Cross-type relative links are now correct in the package and repository view and wrong in the deployed view, because rules and commands deploy as instructions and prompts. That direction is deliberate — the package is what a human browses on github.com and what a plugin install materialises — and nothing functional rests on it, since both lifecycle Instructions declare applyTo: "**" and load regardless. Links into the repository-only reference/ were already dead in the deployed tree, so this widens an accepted condition rather than introducing a new class of defect.

    README.md and AGENTS.md follow. The Folder Structure section now shows the deployed tree and the package layout side by side instead of conflating them, and the Agent plugin install path was rewritten: it previously told the reader that "the plugin format does not carry .instructions.md files or hooks, so this path gives you agents and skills only", which the migration makes false in the one direction that matters — hooks are exactly the guardrails a plugin-only user was silently missing. It now also names the two things worth knowing before choosing that path: bundled hooks execute locally and should be reviewed, and rules/commands registration is unconfirmed.

Fixed

  • Install-CopilotAtelier -WhatIf no longer throws (2026-08-27). A dry run aborted with a terminating ItemNotFoundException from Get-ChildItem: Cannot find path '...\CopilotAtelier' because it does not exist. The legacy-directory sweep enumerated the canonical target unconditionally, but under -WhatIf the target tree is never created (the New-Item/Copy-Item calls that build it honour ShouldProcess and are skipped), so the enumeration hit a path that did not exist. The sweep now runs only when the target is present, which matches how the rest of the function already guards optional paths with Test-Path. Covered by a regression test that asserts -WhatIf neither throws nor creates the canonical target.

4.0.0 - 2026-08-26

Added

  • software-architect Custom agent (2026-08-26). The pipeline in Agents/README.md opened at "Phase: Code Development". Eleven agents covered build, verify, diagnose, document, research, and train, and none owned the phase where the requirement is still text — which is the phase Brooks argues is the cheapest place to fix a defect. The gap was not theoretical. Skills/grill-me/SKILL.md has shipped a forty-to-hundred-question adversarial requirements interview since 2026-07, and inside the Software Engineer agent it loses every time it is tried, because a Skill is advisory content while the Custom agent body is mode instruction: that body directs the model to "not ask for confirmation when the next action is reversible" and opens its loop on "Start from the named file, symbol, failing behavior". Two directives point in opposite directions and only one of them is a mode instruction. Making the interview a persona rather than a suggestion is also what the platform recommends — VS Code documents a reduced-tool planning agent as the counterpart to an implementation agent, and Planning → Implementation as the canonical handoff — so Agents/software-architect.agent.md hands off to software-engineer on sign-off and to security-reviewer for a pre-code threat model, and the Software Engineer agent gained the return path for a requirement gap it cannot resolve locally.

    What the toolset buys is real but bounded, and is recorded as such rather than sold as a sandbox. Thirty-one tools against the Software Engineer's forty-five: runTests, codeInterpreter, execute/runTask, execute/createAndRunTask, execute/runNotebookCell, edit/editNotebook, edit/rename, read/testFailure, and the four vscode/* scaffolding tools are gone. edit/editFiles and execute/runInTerminal stay, and the reason is worth stating because the alternative looks stricter and is not: Post-flight requires a Memory Bank update, a changelog entry, and a local commit on every Substantive turn, so an agent stripped of those two tools would violate its own deployed lifecycle once per turn. The guarantee is therefore not that the agent cannot write code — it is that every sanctioned validation path is absent, so it cannot close the Definition of Done on a code change and the only productive exit is the handoff.

    The rule that keeps it selectable is that the interview scales. Ninety questions about a new switch parameter teaches a user never to open the agent again, so depth is chosen from blast radius — the full twelve categories for a new system, contract, or irreversible schema decision; a named subset for a contained change; and an explicit "no interview" path for an unambiguous request with one obvious design — and the chosen depth and its reason are stated in the first reply where the user can override them. gilb-requirements-engineering is chained the moment an unquantified quality word survives, which is the pairing both Skills already cross-reference and which no agent previously ran. Memory Bank ownership moved with the role: projectbrief.md, which holds scope and Acceptance criteria, is now the architect's with the Software Engineer and Technical Writer as co-curators, and Decision records are authored at sign-off rather than reconstructed from the code afterwards.

  • copilot-usage-stats converts tokens into AI credits and dollars (2026-08-25). The Skill shipped hours earlier stating that the cost column "is not populated for subscription-billed requests" and stopping there, which left the question a user actually asks — what did this cost — unanswered and quietly wrong on two counts. GitHub moved to usage-based billing on 2026-06-01: an interaction is priced per token by model and converted to GitHub AI Credits at 1 credit = $0.01, so money is derivable from exactly the columns the Skill already read. And cost is not an unpopulated dollar figure at all — the one non-zero row in this repository reads 0.33 against a Claude Haiku 4.5 session, which is precisely that model's legacy premium-request multiplier, so anything presenting the column as spend reports a request count in a currency symbol.

    Step 5 carries the conversion, and its whole reason for existing is one arithmetic trap: because input_tokens already contains cache_read_tokens, billing the full input at the input rate charges cached tokens at ten times their rate. On this repository's own numbers that is the difference between $71.45 and roughly $450 — cache reads are 95.6 % of input in an agentic workload, so the error is not a rounding difference but an order of magnitude. Rates are fetched from Models and pricing rather than quoted from memory, because they carry expiring promotional footnotes and models enter and leave the table. Four honesty constraints ship with it: recorded cache writes are zero where the store logged reads but no writes, so the figure is a floor; a model missing from the table is left unpriced and bounded rather than given a neighbour's rate; promotional rates expire; and Pro/Pro+ subscribers who stayed on legacy annual request-based billing are billed by multiplier, for whom a token-derived figure is not their bill at all.

    The description widened to cover the conversion, so the trigger set gained two positives for credit and dollar phrasing and a should upgrade to Pro+ or Max negative that holds the line between measuring what was consumed and advising which plan to buy.

  • copilot-usage-stats Skill and the /usage Prompt (2026-08-25). "How many tokens has this project consumed?" had no answer that survived being asked twice, and the obvious design — a hook that tallies usage as turns complete — cannot be built. Four sources were checked before anything was written: no hook event carries usage or a model, on any of the eight (PreToolUse through PreCompact); the transcript JSONL a Stop hook could parse records assistant.turn_end as {"turnId":"0"} and nothing else; and the local session-store.db schema is sessions, turns, session_files, session_refs, checkpoints, with no events table and no token column anywhere. Token data exists only in the cloud store, reachable only through copilot_sessionStoreSql — so the answer is a Skill the model loads on any phrasing, not a command the user has to remember, and a Prompt on Ctrl+K U for the deterministic one-keystroke path.

    Two measured facts are what make the Skill worth loading rather than improvising the query. input_tokens already contains cache_read_tokens, shown by a per-call sequence where each call's cache_read is the previous call's input less a few tokens (89,097 → 89,754/89,094 → 90,950/89,751); adding the two, which is the natural reading of two adjacent columns, roughly doubles the reported figure, and reporting the raw 88 M input for this repository without splitting off the 84.5 M of cache reads describes one conversation being re-read as if it were fresh work. And sessions.repository is not normalized — this repository alone occupies https://github.com/raandree/CopilotAtelier.git, https://github.com/raandree/CopilotAtelier, and raandree/CopilotAtelier, the last with cwd set and the first two with it empty — so an equality filter silently drops most of a project's history and returns a confident, small, wrong number. The Skill matches the bare name against both columns with ILIKE and folds the spellings before presenting a total.

    Scope is deliberately narrow: this reports, chronicle advises. cost-tips keeps token-reduction recommendations, standups, and session search; the six near-miss negatives in the trigger set are drawn from chronicle, agent-evals, subagent-dispatch, and long-running-job-monitor so the boundary is measured rather than asserted. The cost column is documented as unreportable rather than shown — it is 0 for every subscription-billed row, and a spend figure of zero is worse than none.

  • A PreCompact hook checkpoints the session before compaction (2026-08-25). The Memory Bank had a deterministic entry gate and no exit gate. SessionStart probes for the index, but the only durable write point is Post-flight, which runs "before ending the reply" — so a turn compacted mid-run never reaches it and everything the run learned goes with the conversation. The failure is quiet in the worst way: the summary that replaces the transcript still reports that Pre-flight ran, so the agent believes it holds activeContext.md when it does not, and nothing re-applies the routes. Every comparable system treats this as the primary threat — Anthropic injects ASSUME INTERRUPTION: Your context window might be reset at any moment into the memory tool's system prompt, Cline tells the user to update the memory bank before the window fills, Claude Code re-reads the project CLAUDE.md from disk after /compact — while this repository covered it in one Prompt's bespoke section, one manually invoked /session-handoff, and the subagent-dispatch ledger. All model judgement, none general, and the handoff has to be requested before saturation, which is exactly what a mid-turn agent will not do. Hooks/scripts/Write-CompactionCheckpoint.ps1 now writes .memory-bank/session/compaction-<UTC>Z.md before the truncation, carrying the trigger, transcript path, branch, commit, and changed paths, plus a resume protocol. What the hook cannot do is stated rather than papered over: PreCompact supports the common output format only — no additionalContext — so nothing it emits reaches the post-compaction context. The model-facing half is a Compaction recovery section in Instructions/preflight.instructions.md, which survives by construction because Instructions are re-sent with every request. Payload values are flattened and stripped before they enter a file an agent reads back, every failure path exits 0 so a hook fault cannot block compaction, and a workspace without a Memory Bank gets no directory — creating one stays reserved for the memory-bank Skill.

  • Release-tag parity gate (2026-08-25). Two releases shipped without a changelog section and nothing noticed, because the only thing that reads the release history is tests/PluginManifest.Tests.ps1, and it reads the changelog rather than what was actually published. It now reads both. Every non-preview tag reachable from HEAD must have a matching [x.y.z] release section, so an unmerged rollover pull request turns main red instead of accumulating in silence. One exemption is load-bearing: a tag pointing at HEAD is skipped. On a tag push the test job runs before deploy, so at that instant the tag exists and its section cannot — Create_ChangeLog_GitHub_PR only runs after the release is published — and a strict gate would deadlock every release on itself. The exemption lapses the moment one commit lands on top, which is the earliest point at which the rollover could have been merged. Shown to reject before it was accepted: against the unfixed changelog the run was 8 passed, 3 failed, naming v3.0.0 and v3.1.0 and telling the reader which branch to merge; fixed, 12 of 12 pass. A companion assertion settles a question that had been left open — plugin.json.version is major.minor.patch and never a pre-release, because the field is committed, only moves on a release rollover, and is compared as an opaque string by plugin loaders, where 4.0.0-preview0007 sorts after 4.0.0 and would suppress the update to the real release.

  • README catalogue gate (2026-08-24). AGENTS.md has required a Available Skills row in every Skill's atomic change set since 2026-08-12, but nothing checked it, and the drift that accumulated in the meantime was invisible: 36 rows against 45 shipped Skills. tests/SkillCatalogue.Tests.ps1 closes it in both directions — every shipped Skill has a row, every row names a Skill that still ships, and every row carries a description. It parses the ## Available Skills section rather than the whole file, because README holds other tables whose first cell is bold too and a whole-file scan reads **Agents** and **Instructions** as Skills. A count assertion guards the parser itself, so a moved heading fails loudly instead of passing with zero cases. Shown to reject before it was accepted: with one row removed the run is 133 passed, 1 failed, naming agent-evals has a catalogue row; restored, 136 of 136 pass.

  • brand-logo Prompt (2026-08-14). brand-logo-system answers "how do I build the asset set" but nothing started the conversation, and the first question an identity task needs — what should the mark actually show — is the one an agent is most likely to skip and guess. The Prompt front-loads it: search the repository for an existing mark first and, when one is found, ask only whether to keep, refine, or replace it rather than interviewing the user about a decision the repository already made; otherwise interview in two vscode_askQuestions clusters covering subject, visual treatment, character, colour source, wordmark split, and delivery target. Concepts are proposed as 256 px proofs before eleven assets are built from a guess, and the closing rules restate the two claims that cost time in practice: never invent an identity for a project that already has one, and never assert favicon legibility without a 16 px render.

  • brand-logo-system Skill (2026-08-14). Producing a project identity had been done by hand twice, and both runs rediscovered the same three failures. A text substitution over width=/height= to resize an SVG also matches every nested <use> and <rect> and silently scales each one to the full canvas — the fix is to set the attributes on the root element only. The "transparent" assets in the shared logo library are opaque PNGs with a checkerboard painted into their pixels, measured at 0 % transparent pixels, so their transparency cannot be reused and must not be imitated. And a design board that asserts the mark "holds its silhouette" at favicon size is a claim, not a fact: the 16 px render of a detailed mark disproved it, which is why the renderer now takes an optional reduced favicon glyph and a caller-supplied scalability note. The Skill carries the eleven-slot library layout, the dark-mode-means-reversed convention, and a measured verification gate over count, naming, slot parity, canvas size, corner alpha, painted bounds, centring, and ink coverage at 32 and 16 px. scripts/Export-BrandLogoSet.ps1 composes all eleven slots from one .psd1 plus two or three glyph fragments, so a project's only bespoke artwork is its glyph. Proven end to end against AutomatedLab, whose palette and gear-and-flask mark were recovered from the project's own 2025 logo by pixel count rather than invented.

  • Natural-language Memory Bank route-selection evaluation (2026-08-13). Invoke-MemoryBankRouteSelectionEval.ps1 prepares isolated, label-free prompts from real routing cases and grades strict JSON replies against the hidden human labels. Route order does not affect grading, ambiguity is tested through the Full-read fallback, and pass@k plus pass^k separate capability from reliability without putting paid model calls in CI.

    Grading asks the same question the deterministic resolver asks — did anything required go missing — rather than demanding an exact route set. A reply that selects a superset reads more context but loses none, so it is scored as cost instead of failure; only a dropped label or a wrong Full-read fallback fails. Because that criterion is trivially satisfied by naming every route, RecallPercent, PrecisionPercent, and ExtraRouteCount are reported next to the verdict, and ExactReplies still counts the replies that added nothing. No precision floor is set: there is no measured baseline to derive one from, and an invented threshold is what made the exact-set rule unmeetable.

  • Pester 4 to 5 migration tooling in pester-patterns (2026-08-12). A v4 suite does not fail loudly on v5, it fails quietly: setup that ran per test now runs once during Discovery, a mock that covered a Describe now covers only where it sits, and Should -Throw matches with -like. A migrated test can pass while asserting less than it did before, so the work needs a detection sweep and a baseline count rather than a careful read of the diff. scripts/Find-PesterV4Pattern.ps1 parses each file's AST and reports legacy Should, Assert-MockCalled and Assert-VerifiableMock(s), Describe/Context/It wrapped in InModuleScope, $MyInvocation.MyCommand.Path, commands running at file top level or in a block body, and retired Invoke-Pester parameters. The replacements, mock scoping rules, runner parameter map, and completion checklist are in references/migrating-pester-v4-to-v5.md.

    The script reports and never rewrites, because two of its finding classes are legal Pester 5. Against this repository's own suite it returns zero BlockBodyCommand findings and 18 TopLevelCommand findings, every one of them a deliberate discovery-time assignment feeding -ForEach. That ratio is the point: a detector that called those defects would be ignored within a day.

    The description is deliberately untouched, so the trigger surface is unchanged and no eval sweep is owed — which also means a bare "migrate my Pester 4 tests" still routes on the old wording, where sampler-migration also claims Pester 4 to 5. That boundary is recorded in both new query sets rather than papered over, and the first sweep settles it.

  • Trigger coverage gate and a first cluster of query sets (2026-08-12). One Skill of forty-four had labelled trigger queries, so forty-three descriptions had never been measured for discovery at all. tests/SkillTriggerCoverage.Tests.ps1 makes that a tracked debt instead of an invisible one: every Skill needs a query set or an entry on a documented uncovered baseline, the baseline may only shrink, a Skill that gains a set fails until its entry is removed, and a query set that outlives its Skill is caught as an orphan. Each set is checked for unique ids, valid splits, and at least three positives and three negatives with both halves populated.

    Five sets ship with it — pester-patterns, test-driven-development, sampler-build-debug, sampler-framework, sampler-migration — chosen because they are the cluster most likely to collide with each other. Every negative is a near miss lifted from a sibling Skill rather than an unrelated topic, so a description edit that widens the net is penalised instead of rewarded. Thirty-eight Skills remain on the baseline; none of the five sets has been run yet, so the gate claims only that the queries exist, not that the descriptions pass.

  • A secret scan and frontmatter gates for the other three Customization types (2026-08-12). tests/SecretScan.Tests.ps1 scans the payload, the module source, the build, and the Memory Bank for six high-signal credential shapes — private key blocks, GitHub classic and fine-grained tokens, AWS access key ids, Azure storage account keys, and Slack tokens. Generic password = "..." matching was rejected: it fires on placeholders and parameter names throughout the Skills, and a scanner that cries wolf gets suppressed rather than fixed. It carries a planted-credential test, because a gate never shown to reject a bad input is a green build with nothing behind it.

    tests/CustomizationFrontmatter.Tests.ps1 closes the gap left by SkillFrontmatter, which only ever covered Skills: Custom agents need a slug name, a description, and a model array with at least two entries so a model retirement degrades instead of breaking every agent; Instructions need a non-empty applyTo, without which they never auto-apply; Prompts need the description the picker shows.

  • run-trigger-evals.ps1 -Dispatch Batch, now the default (2026-08-11). Execute mode was a sequential foreach over N queries by R repetitions, which turned pure network latency into wall-clock time for no benefit; the run the harness actually wants is N by R by M model tiers, and 20 x 3 x 3 = 180 sequential calls is not practical. The sweep now goes through Invoke-ShpBatch, which dispatches items concurrently in a bounded runspace pool. Measured on the identical pinned 69-call sweep: 26.3 s batched at -ThrottleLimit 4 against 103.9 s sequential, for the same money (0.7623 USD against 0.7659 USD). -Dispatch Sequential is kept, and documented, so an older run stays reproducible and a ShellPilot without Invoke-ShpBatch still works.

    Three consequences of the batch contract are now enforced rather than assumed. Every item is dispatched with -History @(), so the batch neither reads nor writes a session conversation and the Clear-ShpChat reset the sequential path depends on is gone from it — a test asserts zero resets under batch and exactly one per prompt under sequential. A failed item arrives as data with Success false instead of aborting the sweep, so the harness's hand-rolled try/catch is no longer what provides isolation. And results arrive in completion order, so replies are correlated on the item Id: the test stub returns results reversed on purpose and asserts the content of every reply file, because a position-correlated harness would file every answer under the wrong query and still look successful.

    The -Temperature guard now probes the command that will actually dispatch, so a ShellPilot whose Invoke-ShpBatch predates the parameter is named as such rather than passing because Invoke-Shp happens to have it.

    Concurrency did not change what is measured, and the control proves it. Batched and sequential sweeps of the same description graded train 15/15 both ways and validation 7/8 batched against 6/8 sequential. The difference is not dispatch: two sequential runs of that same description disagreed with each other by just as much, scoring pos-09 at 1/3 and then 2/3, and pos-06 at 3/3 and then 1/3. -Temperature 0 reduces the judge's run-to-run variance but does not remove it, so any query sitting near the 0.5 trigger threshold moves between runs whichever way it is dispatched.

  • Two conformance guards the audit found missing (2026-08-11). tests/PluginManifest.Tests.ps1 validates plugin.json, which until now nothing in the build or the test suite read at all. It asserts a loadable name, that both declared component paths resolve to directories holding the artifacts they claim, that version equals the most recent released CHANGELOG.md section — the field is hand-maintained while GitVersion sets everything else, and both VS Code and the CLI detect a plugin update from it — and that the Agent Plugins $schema is not declared while the skills folder is capitalised. That last one guards a silent failure rather than a present defect: without $schema the manifest loads in the legacy Copilot format, where "skills": "Skills/" overrides the default component path and is the only reason a capital-S folder is discovered. Agent Plugins 1.0 ignores those fields and reads skills solely from a lowercase ./skills, so adding the schema without renaming the folder would drop all 44 Skills with no error. The manifest also gained the license, repository, homepage, and keywords fields the format supports and it had left empty.

    tests/SkillFrontmatter.Tests.ps1 turns the over-budget baseline from a list into a map of Skill to current body length. The list could already never grow, and a Skill could never leave it silently, but a Skill already on it could grow without limit — pester-patterns at 796 lines could have reached 1,200 unchallenged. Each entry is now a high-water mark, so the only ways forward are splitting into references or a deliberate, reviewed increase.

    The same file gained the equivalent ratchet for descriptions, where the failure is worse: the GitHub Copilot CLI drops a Skill whose description passes 1024 characters and reports nothing. Eight descriptions were already past a 1000-character soft cap and authenticated-web-extraction stood at exactly 1024 — one added character from a silent drop, with only the hard cap to catch it and nothing to warn on the way up. Each is pinned to its current length, so crossing the soft cap is now a deliberate baseline edit rather than an accident during trigger-eval optimisation, which is precisely when descriptions grow.

  • run-trigger-evals.ps1 -Temperature, and the measurement it unblocked (2026-08-11). The harness had no way to pin the judge, so a query that scored 1 of 3 could not be told apart from a reliable trigger that got unlucky, and every partial score was uninterpretable. -Temperature is omit-or-send — bound, it is forwarded to every judge call; unbound, nothing is sent and the backend default applies, so an existing run's operating point does not move. 0 is a meaningful temperature, so binding rather than a default is the only safe test. tests/TriggerEvalHarness.Tests.ps1 covers the parameter surface, the range rejection, forwarding, and the omitted case.

    The note in .memory-bank/activeContext.md that this work was "blocked on ShellPilot exposing -Temperature" was half right and the other half matters: ShellPilot has exposed Invoke-Shp -Temperature since commit c89f14a, but the build installed on this machine is 0.4.0 and predates it, so Import-Module ShellPilot silently yields a judge that rejects the parameter. The harness must be run against a ShellPilot new enough to accept it; importing the built module by path is the reliable way.

    Paired runs against the same 44-skill catalogue, the same claude-haiku-4.5, and the same description (57b2cf9), 18 queries by 3 repetitions, 54 of 54 succeeding both times for 0.60 USD each:

    trainvalidationfalse negatives
    -Temperature 010/10 (100%)6/8 (75%)pos-07, pos-09
    unpinned10/10 (100%)5/8 (62%)pos-06, pos-07, pos-09

    Thirteen points of validation score were sampler noise. pos-06 scored 1.00 pinned and 0.33 unpinned — it is not a description defect at all, and without pinning it would have been "fixed" by editing a description that was already working. That single row is the argument for the parameter.

    Two failures survive pinning and are therefore real. pos-07kannst du aus dieser Anleitung einen wiederverwendbaren Skill bauen? — scores 0 of 3 both pinned and unpinned: a hard, repeatable miss on a German-language request, even though the description already says the skill applies when the user does not say "skill". pos-09should this be one skill or split into references? — scores 1 of 3 both ways. Both are plausibly costs of the category-level rewrite: USE FOR: previously carried the literal phrases split into references and body too long, and now carries restructuring an oversized body into references, which is a description of the action rather than of the question a user asks. The rewrite made the Skill pass its own rule; these two rows are what it cost. Iterate on train only.

    Two caveats on the method itself, both found while running it. -Temperature 0 reduces variance but does not remove it — pos-04 scored 0.67 pinned, so a partial score is still possible without a seed, which the harness does not expose. And the grader matches ^\s*SELECTED:\s*<name>\s*$, so a judge that answers in prose is scored identically to one that picks the wrong skill; at least one reply in these runs was prose. A format violation and a trigger miss are not the same failure and should not share a bucket.

  • Trigger evals, the formal eval artifact schema, and a reference-validator build gate (2026-08-11). Three things landed together and none of them had a changelog entry until this one; the omission is itself the point, and is recorded in .memory-bank/progress.md. agent-evals gained scripts/run-trigger-evals.ps1, which measures the prior question to output quality — does the skill get selected at all — using labelled positive and near-miss negative queries on a 60/40 train/validation split, and it gained references/eval-artifacts.md, which documents the evals.json / grading.json / timing.json / benchmark.json / feedback.json set the open standard specifies and which the skill body had never mentioned despite sample files sitting in assets/.

    Judge isolation needed a fix rather than a set of switches. Invoke-Shp seeds from and writes back to a module-scoped conversation, so a judge loop accumulates every previous prompt and verdict; a 54-call run failed calls 19 through 54 with model_max_prompt_tokens_exceeded once the accumulation passed the context window, never recovered because a failed call does not write back, and looked transient only because a fresh process starts empty. Clear-ShpChat before each call restores genuine per-call isolation. The measurement taken through the contaminated run — train 10/10, validation 8/8 — is withdrawn. After the fix, against the live 44-skill catalogue with claude-haiku-4.5, 18 queries by 3 repetitions, 54 of 54 calls succeeded for 0.60 USD: train 9/10 with one false negative, validation 6/8 with two. Validation trailing train by 15 points on positives is a generalisation gap rather than noise at that sample size.

    tests/SkillsRefValidate.Tests.ps1 runs the upstream skills-ref validator over every Skills/*/SKILL.md, so the repository's own reading of the specification is checked against the specification. The validator is pinned to upstream commit 69ef37e and fetched through uv run --with, batched into one process by .build/skills-ref/validate_skills.py because per-skill invocation costs about 50 seconds against about 2 for the batch, and run with PYTHONUTF8=1 because it otherwise inherits the Windows ANSI code page and dies on the first em-dash. 42 skills report clean; citation-integrity and social-signal-sweep are baselined as known divergence, since context: fork is a real GitHub Copilot feature the open specification does not define. Rationale in Decision 19.

  • long-running-job-monitor gained an unprompted chat heartbeat (2026-08-10). The Skill already prescribed a "~5-minute cadence" and Start-JobMonitor.ps1 already sampled on one, but it sampled into a .status file nobody opened, so a job that ran for hours left the chat pane silent and a healthy job was indistinguishable from a dead one. The cadence was never the missing piece; delivery was. The Skill also asserted that the agent "cannot self-schedule a timer", which turned out to be false and was the reason nobody had tried. Measured during authoring: an async command started at 20:23:59 UTC and completing at 20:26:59 UTC produced an agent turn with no user input at all. A completion notification spawns a turn, and an async command does not block the chat, so a periodic report costs a request but never costs responsiveness.

    Start-JobHeartbeat.ps1 arms one tick, waits, and emits a measured summary the agent renders as its status line. Three rules in it are load-bearing. Run the timer in async mode and never through the detached launcher — a fully detached process is invisible to the harness, emits no completion notification, and silently never wakes the agent, which is the exact opposite of the rule that governs the job and the sampling sidecar. The state file stores metadata only: it is re-read and acted on at every wake, so a probe scriptblock persisted there would be a durable local code-execution sink, and a regression test now fails if one ever appears. Every reported value is measured rather than recalled — the first smoke run reported elapsed=120m on a job that had just started, because [datetime]::Parse returns Kind=Local and the later conversion subtracted the local offset a second time; the session that produced this feature also opened four replies with a timestamp that was an hour wrong, which is the same failure in the model rather than in the code.

    The interval defaults to 10 minutes, is settable from the prompt, and follows a 1x, 1x, 2x, 3x, 6x backoff ladder, capping an eight-hour job near nine wakes instead of 48; re-arming with the same interval does not restart the ladder, while a genuine retune does. A sliding reset redefines the cadence as "never more than N minutes without status" rather than "status on a fixed grid": any status line shown between ticks advances the anchor, so the pending tick reports Redundant and the agent re-arms for the remainder instead of repeating itself — during active conversation ticks never fire, because status is already flowing. Where no progress probe exists the verdict reads status=WORKING(low-confidence: no progress evidence), because process liveness alone also describes a hung job, and unavailable fields read n/a rather than being invented. Chain integrity is the residual risk: each tick must arm the next, and one missed re-arm ends the heartbeat silently while the user still believes it is watching. Every status line therefore publishes its next due time, and references/heartbeat-protocol.md records the Stop-hook enforcement option — with the warning from the VS Code docs that a blocking Stop hook consumes credits and loops indefinitely unless stop_hook_active is checked and the block count capped — plus the batch pre-arm fallback if that hook proves unreliable. tests/LongRunningJobMonitor.Tests.ps1 adds 18 tests covering the ladder, the sliding reset, cancellation, the measured-elapsed regression, and the never-persist-a-probe guard.

    Live smoke testing found one defect and settled the open design question. The defect: there was no way to cancel an armed tick, so "stop watching" did not actually stop anything and every job completion cost a spurious wake. -Stop now cancels the pending tick, matching the recorded process start time before killing so a recycled process ID is never a target. The design question was whether a Stop hook can enforce the chain, and it can — a guarded one-shot hook returning decision: "block" forced a turn that would otherwise have ended, which removes the need for the batch pre-arm fallback. Verified live across a four-minute stand-in job: unprompted wakes at 21:21:58 and 21:23:12, the ladder stepping 1m, 1m, 2m, the sliding reset marking a tick Redundant with 0.61 minutes remaining after a mid-interval message, immediate completion reporting from the job's own notification rather than a delayed tick, two concurrent jobs waking as independent turns nine seconds apart with no cross-contamination, and a separate process reconstructing elapsed=45m plus the ladder position from the state file alone.

Changed

  • Every Instruction now declares a description, and the twelve legacy standards files were held against the authoring rules (2026-08-26). Yesterday's re-verification of Instructions/copilot-authoring.instructions.md established that VS Code also activates an Instruction by semantic match of its description against the current task, not only by applyTo glob. Twelve of the sixteen shipped Instructions declared no description at all, so a path match was their only route into a conversation — versioning.instructions.md could not be reached by a question about pre-release labels unless a .psd1, GitVersion.yml, or CHANGELOG.md happened to be in scope, and pester.instructions.md was unreachable while writing the first test in a repository that had none. Each of the twelve now declares one, and the gate is a per-file case in tests/CustomizationFrontmatter.Tests.ps1. Shown to reject before it was accepted: against the previous commit it fails 12 of its 16 cases and names each file; with the descriptions in place the four affected suites run 114 of 114.

    The same pass held those twelve against the rules the authoring Instruction states. Five ## Summary Checklist sections and three ## Best Practices Summary sections restated, as checkmark bullets, rules the same file had already given in full — several hundred lines of pure duplication loaded into every context the glob matched. They are gone, together with the introductory explanations the Strict tier forbids outright: what a changelog is and who needs one, that Markdown was created by John Gruber in 2004, what YAML stands for, what GitVersion does. Two trailing link farms went with them, but only after confirming that the normative links they carried — Keep a Changelog, Semantic Versioning — already appear inline in the body. csharp.instructions.md's ## Additional Resources was deliberately kept and its bare URLs converted to proper links, because it holds that file's only pointers to the Microsoft, OWASP, and Roslyn sources, and linking to authoritative docs is what the rules ask for in place of explaining. The 78 surviving decorative check and cross marks were dropped from bullets whose lead-in already reads Always Include, Never Include, or Do NOT increment version for, and from two comparison tables where they were replaced by the words they stood for. Net across the twelve: 355 lines removed, with versioning down 121, markdown 71, yaml 65, and changelog 56.

    Two defects surfaced while reading. versioning.instructions.md closed its changelog-integration section with "See markdown.instructions.md for detailed changelog management practices", pointing at the wrong file in a repository that ships changelog.instructions.md. And two applyTo lists carried patterns fully subsumed by another pattern beside them — **/azure-pipelines.yml inside **/azure-pipelines*.yml, and **/*.Tests.ps1, **/build.ps1, **/RequiredModules.psd1, and **/Resolve-Dependency.psd1 inside **/*.ps1 and **/*.psd1 — removed without changing what either file matches. description moved from recommended to required here in the Instruction schema, now that every shipped file declares one and a test enforces it. Left open and reported rather than changed: powershell-execution-safety.instructions.md still claims **/*.yml, which attaches build-execution rules to every YAML file in a workspace; narrowing it would change what the guardrail covers and belongs in its own change.

  • Every Custom agent file is now named after its slug (2026-08-26). Seven of the twelve agents were addressed by two different names at once. On disk they were display names — Security & Quality Assurance Agent.agent.md, Technical Writer & Documentation Agent.agent.md — while the name: frontmatter every one of them declared was already security-reviewer and technical-writer, which is what a handoffs.agent value, an agents: allow-list entry, and a Prompt's agent: key resolve against. Nothing was broken by that split, but everything that had to write the path paid for it: eight changelog links carried %20-encoded targets, the baseline map in tests/SharedLifecycle.Tests.ps1 was keyed on the display name while the handoffs it asserted named the slug, and the catalogue in Agents/README.md had to spell both. The five agents added since career-coach had all been named the other way, so the convention was already settled in practice and only the older files disagreed. Renamed with git mv, so history follows the file rather than restarting at the rename.

    The guard is a filename-to-frontmatter equality assertion in tests/CustomizationFrontmatter.Tests.ps1 rather than a lowercase regex, because lowercase is the symptom and the two-addresses problem is the defect: a file whose stem is agent-two and whose name is agent-three passes any casing check and still cannot be found by anyone who read the other spelling. No agent identity changed — every name: value is untouched — so handoffs, subagent allow-lists, Prompt targets, and any user muscle memory for the dropdown all keep working.

  • skill-creator split into a body plus two references (2026-08-25). The Skill that carries this repository's progressive-disclosure rules was the worst offender against them: 492 lines against the 500-line budget, zero reference files, and a Splitting an oversized SKILL.md section it had never applied to itself. The budget is not a house rule — Anthropic's authoring guide says to keep the body under 500 lines and to "split content into separate files when approaching this limit", and 492 is approaching it by any reading. At 8 lines of headroom the next real addition would have landed the file on the shrink-only $overBudgetBaseline in tests/SkillFrontmatter.Tests.ps1, turning a fixable problem into recorded debt. What moved was chosen by a principled test rather than by line-count triage: Anthropic's own first principle is "only add context Claude doesn't already have", and Instructions/copilot-authoring.instructions.md independently forbids restating subject matter instead of linking to authoritative docs — so the material that went to references is the material upstream already teaches and the model already knows. references/authoring-patterns.md took degrees of freedom, structural patterns 2 to 8, the Claude-A / Claude-B loop, model-tier testing, and the generic anti-pattern list; references/scripts-and-evaluation.md took solve-don't-punt, the non-interactive caller interface, plan-validate-execute, the six-step trigger-eval procedure, and evaluation-driven development. What stayed is what only this repository knows or what nothing upstream says: the six-step frame, the Gotchas pattern, Match the form to the failure, behavioural enforcement, the cross-skill overlap audit, the repository's gates and registration model, and the reconciliation of the third-person-versus-imperative contradiction between two live upstream guides. The condensed trigger-eval section gained the repository detail it had been missing — the committed Skills/agent-evals/assets/trigger-queries.<name>.json asset and the gate that fails without it. The description is deliberately untouched, so the trigger surface is unchanged and no eval sweep is owed; both references are one level deep and the longer one carries the required ## Contents table of contents.

  • copilot-authoring.instructions.md re-verified against the current platform documentation (2026-08-25). The file is the schema every Customization in this repository is authored from, and it had drifted far enough that one of its rules was contradicted by the repository's own shipped files: prompts were documented as requiring agent: agent | ask, while eight of twelve Prompts/*.prompt.md name a Custom agent and peer-review.prompt.md declares no agent at all. The platform treats the key as optional and accepts ask, agent, plan, or a Custom agent name, so the rule was wrong rather than merely strict. The Instruction schema gained the field that changed how Instructions activate — VS Code now also matches an Instruction by semantic similarity between its description and the current task, so a file whose real trigger is a task rather than a path needs one, and only three of fifteen shipped Instructions had one. The agent schema gained target, mcp-servers, handoffs.model, the agent-tool prerequisite for declaring agents, and the chat.useCustomAgentHooks gate on agent-scoped hooks. The hook contract gained the half it never had: exit codes were documented but the stdout JSON was not, so continue / stopReason / systemMessage, hookSpecificOutput, most-restrictive-wins, and the stop_hook_active guard that keeps a blocking Stop hook from billing turns indefinitely are now stated — as is the constraint that shaped Decision 0021, that only four of the eight events can inject additionalContext. Two portability traps are named because this repository ships to Claude Code and the Copilot CLI as well: a Claude-format matcher is parsed and then ignored, and tool input keys are camelCase in VS Code where Claude uses snake_case. Where the repository is deliberately stricter than the platform the schema now says so, rather than presenting a house rule as a platform requirement. The file then failed the rule it had just written: it declared no description of its own, and its applyTo matches only files that already exist — so the moment it is most needed, when the question is still "should this be an Instruction, a Skill, or a Hook?" and nothing has been created yet, was the one moment it could not activate. It now declares one. Nothing catches this class of omission: tests/CustomizationFrontmatter.Tests.ps1 asserts applyTo on every Instruction and never description, because a description is genuinely optional for a path-triggered rule like csharp or yaml and only becomes load-bearing when the real trigger is a task.

  • german-tax-research reads the operative sentence and aggregates both directions (2026-08-24). Two failure modes from real case work produce a confidently wrong number rather than a visible error, which is why neither shows up as a mistake until an examiner finds it. The first is classifying a document by its label: a title, subject line, filename, or category column is metadata someone else wrote for another purpose, and one session produced three counterexamples in a row — an e-mail headed Your sessions at NIC Cloud Connect 2023 whose body read have not been accepted, a file named 240108 Überschussabrechnung that was the December statement rather than a January one, and a Zinsbescheinigung column that looked like sonstige Kosten but held the Tilgung. The second is summing a transaction set with a sign filter, which makes the refund, reversal, credit note, or Storno structurally invisible — precisely the entry that changes the answer. A flight showing 1.725,05 € in charges had cost 848,12 € once the airline's refund of one of two bookings three days later was counted. Both are now sections under the evidence rules, each with a matching entry in the stop-and-re-enter red-flag list. The frontmatter description is deliberately untouched, so the trigger surface is unchanged and no eval sweep is owed; the body stays inside the 500-line budget, and the Skill remains on the documented SkillTriggerCoverage uncovered baseline.

  • brand-logo-system covers integrating the assets into a project (2026-08-14). The Skill produced a library set and stopped, so wiring the mark into a repository was improvised every time it came up. Step 5 now carries it, and carries the parts that are not guessable: a <table> cannot give the README a borderless two-column header because github.com draws a 1px border on every cell and its sanitiser strips the style that would remove it, so the mark is floated and closed with <br clear="left">; the wordmark replaces the <h1> rather than sitting above one, which is why MD041 stays disabled for the file; a package IconUri must be a direct image URL, because a repository URL is accepted and then silently shows a placeholder. Integration asks which repository first — the library holds many projects and a session usually has several open, so "add the logo" names no target and a brand commit in the wrong project is noise its owner has to find and revert — and a project that is not the user's to change gets a block to paste instead. The trigger queries said the opposite of the new boundary: "add an IconUri to the module manifest" was a negative pointing at sampler-framework and is now a positive, joined by header-layout, placeholder-icon, and social-preview positives, and by a screenshot-in-the-README negative that keeps windows-gui-screenshot-capture from being swallowed. The brand-logo Prompt gained the matching phase, and its delivery-target option no longer names a folder the Skill contradicts.

  • AGENTS.md states the atomic change set for a Skill and a Custom agent (2026-08-12). A Customization is never one file, and a half-added one leaves the catalogue, the trigger coverage, and the changelog disagreeing with each other. The new Atomic change sets section names every artifact that has to move in the same commit for each, and tables which test catches which kind of drift — so the answer to "what else does this change need?" is in the house rules rather than in a reviewer's memory.

  • pester-patterns split into a body plus two references (2026-08-11). The body was 796 lines against the 500-line progressive-disclosure budget and had been carried on the SkillFrontmatter over-budget baseline rather than fixed. It is now 149 lines. What stayed is what an agent needs on every Pester run: pattern 0, run tests through the fully detached launcher, and pattern 14, helpers used inside It must live in BeforeAll. What moved is what it needs sometimes — patterns 1 to 3 into references/mocking-external-dependencies.md and patterns 4 to 13 into references/testing-powershell-constructs.md, both one level deep, both keeping their original numbers so existing references still resolve. The frontmatter description is untouched, so the trigger surface is unchanged and no eval re-run is owed. Its baseline entry is removed in the same change, so the gate proves the fix rather than recording the intent; nine Skills remain baselined, german-legal-research at 780 lines the worst of them.

  • .memory-bank/systemPatterns.md curated from 106 lines to 86 (2026-08-11). It sat 4 lines under its 110-line budget and warned on every build, while the Decision index that must stay grows by a line per record. The repository tree at the top is what went: a changing inventory of the working tree, duplicating techContext.md's module layout, deployment boundary, and discovery model, and contradicting the file's own closing rule to index durable relationships only. The build warning is gone and the routing reduction improved from 55.69 % to 56.11 % against its 50 % floor — curation moves that gate the right way, which is the direction to check before any Memory Bank edit.

    Two recorded premises did not survive inspection and are corrected here: techContext.md's per-test-file inventory had already been curated away, and the routing gate is not at 49.57 % with roughly 1 KB of headroom.

  • The USE FOR: convention is a category list, not a keyword dump (2026-08-11). agentskills.io states that adding the verbatim wording of queries that failed to trigger is overfitting, and that the fix is to name the general category those queries represent. The house convention said close to the opposite, and Reference/howto-write-skills.md asserted as fact that the selector "matches lexical overlap, not semantics" — a claim nobody here can evidence. USE FOR: and DO NOT USE FOR: both stay, because the boundary clause maps cleanly onto upstream's advice and has no upstream equivalent worth losing; only the framing changes. The lexical-overlap assertion is replaced with the honest position that selector mechanics are undocumented, so guidance must hold either way, and overfitting joins the anti-pattern list cross-referenced to the trigger-eval loop. All four files that state the convention now agree: copilot-authoring.instructions.md, definition-of-done.md, howto-write-skills.md, and skill-creator.

    skill-creator and agent-evals are migrated to the rule they teach; the remaining 42 descriptions are deliberately left, because generalising a description near the 1024-character cap is a regression risk that wants measurement rather than enthusiasm. skill-creator went from a 12-comma verbatim list including "skill not triggering" and "description over 1024 chars" to seven named categories, 995 characters to 990. agent-evals also gained the two capabilities its description had never advertised — trigger-rate measurement and the with/without delta — within the same cap.

  • The category rule was drawn too wide, and the trigger evals had already priced it (2026-08-11). The preceding entry conflated two upstream rules that are not the same. The specification asks a description to carry "specific keywords that help agents identify relevant tasks"; optimizing skill descriptions says only that adding keywords from failed queries is overfitting. The rewrite dropped the domain vocabulary along with the overfitted phrasings, and the measurement in this same release records the bill: pos-09 (should this be one skill or split into references?) fell to 1 of 3 pinned and unpinned once split into references left USE FOR:. The rule now reads "keep the domain vocabulary, drop the failed-query wording" across the same four files.

    Two contradictions in Reference/howto-write-skills.md go with it. Its rule 2 still said "Third person, always" while skill-creator had already reconciled the two upstream guides as third-person voice for the capability and imperative for the trigger — and the primer's own description template three sections later prescribes the imperative clause, so the file disagreed with itself. Its canonical-source list was Anthropic-first with agentskills.io at position 7 as a bare root link, which no longer matches where skill-creator says the authority lives; the open standard's five authoring pages are now enumerated first, with the VS Code client surface added for the fields the standard does not define.

  • skill-creator teaches the script interface, not only the script (2026-08-11). agentskills.io has five skill-creation pages and the Skill cited four; using scripts in skills was missing, and with it every rule about how a script talks to its caller. The repository ships roughly thirty of them. The new Design the interface for a non-interactive caller section carries the hard requirement first: an agent runs in a non-interactive shell, so a TTY prompt, password dialog, or confirmation menu blocks until the harness gives up. Then --help as the interface the agent actually reads, actionable errors, structured data on stdout with diagnostics on stderr, bounded output because harnesses truncate somewhere around 10-30 KB and lose the rest, distinct documented exit codes, idempotency with -WhatIf/--dry-run, and dependencies declared inside the script (PEP 723 with uv run, or #Requires -Module) rather than in prose the agent may never read. The frontmatter section also stops implying that name and description are the only fields: it now points at copilot-authoring.instructions.md for compatibility, license, metadata, allowed-tools, argument-hint, user-invocable, disable-model-invocation, and context — and records that context: fork is a GitHub Copilot field the open standard does not define, which is why two Skills sit on the skills-ref divergence baseline. Body 457 to 473 lines against the 500-line budget.

  • subagent-dispatch covers forks, report trust, and the gotchas that defy assumption (2026-08-11). A drift pass against the current Claude Code sub-agents documentation, filtered to what holds on the Copilot surface; Claude Code-specific field names are labelled as such and the portable technique stated alongside each. A fork inherits the whole conversation and shares the parent's prompt cache, which makes it cheap for a context-heavy side task and worthless for any check whose value depends on independence — a fork has already read your reasoning and your answer, so its agreement proves nothing. A subagent's report is untrusted data: harness-side output scanning flags text that imitates harness output or names a permission setting, but a flag is a notice and does not stop a tool call the report talks you into. The new Gotchas section records that an omitted model means "inherit the controller's", usually the most expensive one in the session; that a background run silently resolves to a smaller built-in tool set, so the same definition behaves differently having never been edited; that cd does not persist between a subagent's tool calls; that preloading beats discovery for a convention; that per-subagent memory is unreviewed by construction and is never the ledger; and that nesting and concurrency caps are silent until hit.

Fixed

  • The QC Inspector Agent had no heading in the agent catalogue (2026-08-26). Agents/README.md numbered its agents 1 through 11 with 7 missing entirely: the QC Inspector section began at a bare **Role**: line under the preceding separator, so it rendered as a continuation of the Tax Researcher entry and was unreachable from any table of contents. Found while renumbering the catalogue to seat the new software-architect entry at position 1, and repaired in the same pass rather than left as a gap in a sequence actively being edited.

  • The pipeline rejected a GitVersion run that had succeeded (2026-08-25). The first pull request this repository ever opened failed in Package Module before a single test ran, and GitVersion was not the thing that broke — it exited 0 and returned valid JSON. Two independent defects stacked on top of each other. GitVersion.yml left the feature and hotfix regexes unanchored, so ai/fix-manifest-bom-ps51 matched both of them — ai/ satisfies feature, and fix- anywhere in the name satisfies hotfix — and GitVersion resolves that by taking the first match and warning about the rest. It writes that warning to standard output, the same stream it writes its JSON to. .github/workflows/ci.yml then required the captured output to start with {, so a five-line warning turned a healthy version calculation into dotnet-gitversion exited with code 0 and did not return JSON. — the guard added on 2026-07-29 to stop the step destroying the evidence of a failure was itself manufacturing one. Both halves are fixed at the source: the two regexes are anchored, and the step now locates the JSON block within the output, echoes any preamble as diagnostics rather than discarding it, and reports the parser's own message when the block will not parse. The parsing change was exercised against the captured CI output before it shipped — the run CI rejected now yields 4.0.0-PR0021.43, JSON-only output still parses, and output containing no JSON is still rejected. A -ForEach gate in tests/QA/module.tests.ps1 asserts that each representative branch name matches exactly one branch configuration, and was shown to reject before it was accepted: against the unanchored configuration the run was 821 passed and 1 failed, naming 'ai/fix-manifest-bom-ps51' matched: feature, hotfix, but got 2; fixed, 822 of 822 pass. Only branch names containing fix were ever affected, which is why the collision survived every earlier ai/ branch.

  • Install-Module reported "not a properly-formed module" on Windows PowerShell 5.1 (2026-08-25). Reported as #20. Every published release since 2.0.0 was affected, and it was diagnosed once before, on 2026-07-29, and left unfixed on purpose: Create_Changelog_Release_Output writes the changelog's release section into the built manifest's PrivateData.PSData.ReleaseNotes and saves the file without a byte-order mark, so a BOM-less file decodes with the system ANSI code page instead of UTF-8 and any non-ASCII character in that prose corrupts into mojibake that breaks the manifest's restricted-language parser. The 2026-07-29 fix dropped the CI leg that caught it instead of touching the manifest, reasoning that nobody installs this module on "an interpreter nobody runs this module on" — a premise #20 falsified: a plain Install-Module -Name CopilotAtelier -Scope CurrentUser under genuine Windows PowerShell 5.1 failed with exactly this error. Test-ModuleManifest against the built 4.0.0 manifest under powershell.exe reproduced it directly, throwing Unexpected token at an em dash inside a German-tax-research changelog entry; Install-Module's wrapper discards that detail and reports only the generic The module 'CopilotAtelier' cannot be installed or updated because it is not a properly-formed module. A new Repair_ManifestEncoding task runs after Create_Changelog_Release_Output and re-saves the manifest with a UTF-8 BOM whenever one is missing, changing no other byte; re-running Test-ModuleManifest against the rebuilt manifest under powershell.exe then passed. tests/QA/module.tests.ps1 gained a regression test asserting the built manifest carries the BOM. The dropped CI leg is not restored in this change; that is tracked as follow-up.

  • plugin.json announced 2.0.0 while 3.1.0 was the published release (2026-08-25). VS Code and the GitHub Copilot CLI detect a plugin update from that field, so for the whole of August it told every installation that nothing had shipped since 2026-07-29 — through two releases. The manifest was not the defect. It follows the newest released changelog section, and the changelog still ended at [2.0.0], so it was faithfully reporting a history that had stopped being updated.

    The rollover was never missing from the release run, which is what made this hard to see: .github/workflows/ci.yml runs Create_ChangeLog_GitHub_PR after every publish, and it worked both times — origin/updateChangelogAfterv3.0.0 and origin/updateChangelogAfterv3.1.0 still carry the commits it produced. Nobody merged the two pull requests. Nothing downstream cared: the task wraps its whole body in a catch that only writes to the build log, so it cannot fail a build, and no test compared what was tagged against what was written down. Merging the two branches today would not have worked either — the second was cut from a main that still lacked the 3.0.0 section, so its roll-up would have filed the July entries under 3.1.0.

    The sections are therefore reconstructed from the two unmerged commits rather than from the commit log, so every entry sits under the release it actually shipped in: 13 entries move to [3.0.0], 5 to [3.1.0], and 31 remain genuinely unreleased. The move was verified line by line against the committed file — zero lines lost, and the only additions are the two release headers and their six subsection headers. plugin.json follows to 3.1.0, and the compare links are filled in. One side effect is worth naming: the [Unreleased] body the release task sends to GitHub drops from 83,271 to 54,733 characters, which restores the headroom under the 100,000-character release-body gate in tests/QA/module.tests.ps1 — the same limit that broke a release on 2026-08-01.

  • The README catalogue lists all 45 Skills (2026-08-24). Nine were missing — agent-evals, agent-security-review, doc-coauthoring, evidence-package-assembly, gilb-requirements-engineering, grill-me, mcp-builder, pswritehtml-reporting, and skill-creator — so a third of the recently added library was invisible to anyone reading the repository, including skill-creator and agent-evals, the two a contributor needs first. The rows are added and the new catalogue gate keeps the next one from slipping.

  • german-tax-research drops the unsupported red flag (2026-08-24). Editing one row of a table without printing its neighbours afterwards shipped in the stop-and-re-enter list on 2026-08-24 and pointed at nothing: neither the Skill body nor its six references teaches that discipline, so the flag named a rule the reader could not look up. It also was not tax-domain material — it describes editing-tool hygiene, which belongs to the general instructions, not to a Skill about Einkommensteuer. The two flags added alongside it stay, because each rests on a measured counterexample. Rather than invent evidence to justify keeping it, the flag is removed.

  • Get-SteuerFrist.ps1 is stored as UTF-8 with a BOM (2026-08-19). The script is the one shipped artifact whose output is German legal text — § 122 Abs. 2 Nr. 1 AO, Bekanntgabe verschoben, the sixteen Bundesland names — and it was written as UTF-8 without a byte-order mark, which PSScriptAnalyzer flagged as PSUseBOMForUnicodeEncodedFile. A BOM-less file is decoded by Windows PowerShell 5.1 as the ANSI code page, which would have turned every section sign and umlaut in the emitted Fiktionsnorm and Hinweise strings into mojibake inside a document destined for a Finanzamt. Only the three BOM bytes were added; the remaining 10,028 bytes and all 252 CRLF line endings are untouched, the script reproduces its documented worked example unchanged (notice 25.02.2026 → deemed notification 02.03.2026 after the Sunday shift → objection deadline 02.04.2026), and it is now the analyzer-clean state the rest of Skills/**/scripts/ is already in.

  • CI resolves the uv install step again (2026-08-12). Every test leg failed before it ran a single job step, at Prepare all required actions: Unable to resolve action astral-sh/setup-uv@v9, unable to find version v9. astral-sh/setup-uv stopped publishing a floating major alias after v7v8.3.2 and v9.0.0 exist as full release tags, refs/tags/v8 and refs/tags/v9 do not — so the reference had never been resolvable. The step is now pinned to v9.0.0, which the workflow comment explains, and the step takes no inputs so the major bump carries no configuration risk. The three actions/* references in the same workflow were checked against the tag API and all resolve.

  • Set-CustomizationLink no longer prompts, discards, or follows a link out of the tree (2026-08-11). Three review findings had been recorded and deferred; all three still reproduced, and the reproductions are now regression tests.

    The Read-Host before replacing a non-empty discovery folder is gone. The function is reachable unattended through the shipped Update-CopilotAtelier -Force path, where a prompt does not fail — it waits forever on a host with no console. The opt-in is now -Force, surfaced on Install-CopilotAtelier and on Setup-CopilotSettings.ps1; without it a populated folder is left alone and the message names the switch. Interactive users who previously answered y in-flight now re-run with -Force.

    A child present in both the folder and the target used to be skipped and then destroyed by the Remove-Item -Recurse that followed. Measured against the previous implementation: a file holding deployed copy, 126 lines was gone after the merge, leaving only repository copy, 106 lines — the same shape as the drift .memory-bank already recorded in the wild, where the deployed copy was the newer one. And Copy-Item -Recurse followed a junction inside the folder, materialising content from outside the tree inside the target.

    Both are closed by one rule, recorded as decision 0020: anything that cannot be merged without losing content stops the merge, and nothing is copied or removed. A child already in the target is dropped only when both sides are files with the same length and the same SHA-256; a directory on either side, a differing file, a child that is a reparse point, and a child containing one at any depth all stop the merge and are named in the report. Newest wins was rejected because a timestamp is not evidence, and source always wins was rejected because it is the defect. The seven new tests create real junctions on Windows and real symbolic links elsewhere, neither of which needs elevation, so the reparse-point cases are exercised rather than skipped.

  • run-trigger-evals.ps1 -Temperature failed 54 times instead of once, and never named the cause (2026-08-11). The [Unreleased] note above records that ShellPilot has exposed Invoke-Shp -Temperature since c89f14a while the build installed on this machine did not; what it could not say is how badly that fails. The installed build was 0.4.0-preview0003, every call died in the parameter binder with A parameter cannot be found that matches parameter name 'Temperature', and the run reported Executed 0/54 ... failures=54 at zero cost — a result indistinguishable from a harness bug. Execute mode now checks once, before the loop, and throws a message naming the resolved build and the fix.

    The check probes the parameter, not the version, and that distinction is the finding. Get-Module reports 0.4.0 for 0.4.0-preview0003, so a minimum-version test written as "ShellPilot 0.4.0 or later" — which is exactly what agent-evals declared in compatibility — passes on the one build that cannot run the parameter. The prerelease tag is where the difference lives, and comparing prerelease strings to decide a capability is a worse test than asking the command what it accepts. The compatibility field now states the real requirement, first shipped in 0.4.0-preview0004, and says why a version test cannot decide it; the guard enforces it. Only what the run needs is checked: with -Temperature omitted, an older ShellPilot is fine and the run proceeds.

    tests/TriggerEvalHarness.Tests.ps1 stands a real module on disk in place of the stale install — ModuleVersion 0.4.0, Prerelease preview0003, no -Temperature — because only a module gives Get-Command the version, prerelease, and base path the failure has to quote, and because a bare function would not reproduce the import that displaces a stub. Three cases: the guard throws before any call and writes not even a prompt file, the message carries the prerelease and the path, and an older ShellPilot still runs when -Temperature is not asked for. The stubs are now re-asserted from held scriptblocks before every run rather than defined once, so a test that deliberately imports a ShellPilot module cannot leak into the next one.

  • The trigger-eval working directory would have shipped to the PowerShell Gallery (2026-08-11). Skills/ is copied verbatim into the built module, and the copy reads the working tree rather than the index, so a gitignored scratch directory is still published. Nothing was gitignored in this case: run-trigger-evals.ps1 documented -WorkDir ./work in all three of its examples, which resolves inside the skill folder, and .gitignore covered only .evalwork/. The next pack and publish would have shipped 108 judge prompts and replies to the PowerShell Gallery. Three controls now stand in the way: the examples point at $env:TEMP, .gitignore covers Skills/*/scripts/work/, and Copy_Customizations_To_Output prunes any work or .evalwork directory from the destination and says so in the build log. tests/QA/module.tests.ps1 asserts the built module carries neither.

  • The skills-ref gate could not fail in CI, and a Skill's dependencies were undeclared (2026-08-11). The gate skips with a visible reason when uv is absent, which is correct on a developer machine and wrong in CI, where no step installed uv and all 45 assertions therefore skipped into a green build. The test job now installs uv with astral-sh/setup-uv, and the gate throws instead of skipping when $env:CI is set. A gate that only ever passes also proves nothing, so a negative test writes a fixture with an invalid name to a temporary directory and asserts the validator rejects it. Separately, agent-evals had acquired hard dependencies on PowerShell 7, powershell-yaml, and ShellPilot without declaring any of them; it now carries a compatibility field and a Harness prerequisites section, and tests/SkillFrontmatter.Tests.ps1 derives the requirement from shipped scripts that Import-Module rather than from a hand-maintained list, which is what let this through.

  • The Linux and macOS CI legs failed on a Windows-only Start-Process parameter (2026-08-11). Should cancel a pending tick and clear the armed process in tests/LongRunningJobMonitor.Tests.ps1 launched its stand-in heartbeat process with Start-Process -WindowStyle Hidden, which raises NotSupportedException: The parameter '-WindowStyle' is not supported for the cmdlet 'Start-Process' on this edition of PowerShell on non-Windows PowerShell. The Windows leg stayed green, so the break only ever showed on two of three matrix legs. The shipped Start-DetachedPowerShell.ps1 had the guard from the start — the test simply never inherited it. The call is now splatted and adds WindowStyle only on Windows, and the script path is built with forward slashes so pwsh -File resolves it on a case where the FileSystem provider is not doing the normalising.

  • windows-gui-screenshot-capture taught that Chromium windows always capture black, and had no branch for a window that is already open (2026-08-11). Surfaced by the simplest possible request — "screenshot the Edge window" — which the Skill answered wrongly twice over. Step 2 stated that "GPU-composited content returns solid black: WebView2 (Chromium), WinUI 3, and UWP", so the documented route to a browser window was Windows.Graphics.Capture and its WinRT interop. Measured on Windows 10.0.26200 with Edge 151.0.4129.72, PrintWindow with PW_RENDERFULLCONTENT returned a fully painted 2560x1540 frame on the first attempt: 13741 sampled pixels, 1968 distinct colours, a 0.1 % near-black ratio, and no fallback. The Skill's own proof-of-concept finding was never wrong — it measured a WebView2 control hosted inside another window, whose pixels a second process composites — but it had been generalised from "this hosted control" to "Chromium", and that generalisation costs a reader the cheap path. Step 2 now separates the hosted-control case from an application's own top-level frame, and replaces the verdict with a ladder: attempt PrintWindow, run the pixel gates, and escalate to CopyFromScreen over DWMWA_EXTENDED_FRAME_BOUNDS and only then to Windows.Graphics.Capture. One machine is not a guarantee, so the text hedges in both directions — the gates decide, not the engine name — and the matching inverse rationalization ("it's Chromium, so PrintWindow is pointless") now sits beside the original one.

    The second gap was structural. Every scene-driving branch assumed the agent launches the target, and the cleanup contract is built on that: keep the Process object, close dialogs, terminate only what the driver started. A window the user already has open inverts it — nothing may be launched, closed, or killed, and the state that matters is whether the window was minimized. Step 3 gains that branch and scripts/WindowCapture.ps1 implements it as the sibling of DialogCapture.ps1 with the opposite lifecycle: Save-OpenWindowCapture re-checks handle ownership with GetWindowThreadProcessId, never discovers by foreground window, sets per-monitor DPI awareness before reading bounds, measures with DWMWA_EXTENDED_FRAME_BOUNDS rather than GetWindowRect — which includes the invisible DWM resize border and drags a strip of desktop into a screen read — runs the ladder, and re-minimizes in finally only a window it restored. Test-WindowCaptureContent is deliberately parameterised rather than fixed, because the Skill already forbids a universal black threshold: a dark-theme frame must stay acceptable at a raised MaximumDarkRatio. The branch also carries the privacy note the demo made obvious, since a browser frame exposes tabs, profile name, and history. tests/WindowsGuiScreenshotCapture.Tests.ps1 adds 13 tests — a solid black capture is rejected even though PrintWindow would have returned true, a dark-theme capture passes at a raised ratio, and the helper is asserted to contain no Stop-Process, PostMessage, .Kill(), or CloseMainWindow against a user-owned target.

  • A workspace .github/hooks/*.json never fired, and nothing said why (2026-08-10). Found while smoke-testing a Stop hook: the file sat in .github/hooks/, one of the documented default locations, and simply never executed — no log entry, no error in the hooks output channel, no diagnostic anywhere. The script itself was correct, proven by piping a synthetic payload into it by hand. The cause is that chat.hookFilesLocations replaces the default location map rather than extending it, so a settings value of { "~/.copilot/hooks": true } silently drops .github/hooks along with the Claude Code locations. The documentation's phrasing — "add an entry for a new location, or set a path to false to disable a location" — reads like a merge, which is what made the failure hard to attribute. Hooks/README.md now carries the symptom, the cause, and both remedies; its previous "Hook never fires" entry covered only a broken deployment link and would not have led anywhere near this.

3.1.0 - 2026-08-07

Added

  • New Prompt audit-case-file (2026-08-05). A drafting session accumulates conclusions, and over a long-running matter those conclusions migrate from summary to summary until they read like established fact. The empirical trigger was a single session on a live case file in which three assertions were checked against the primary corpus for the first time: one was refuted by a message the author had sent himself, one had been framed backwards by the assistant and was in truth the strongest argument available, and one was correct in substance but attackable in its wording against the exact phrase a third party had used. None of the three was careless. All three had survived because nobody had opened the source file since the claim was first written down. The Prompt exists to open them.

    Its load-bearing rule is that the project memory is a finding aid and not evidence — a claim resting only on the Memory Bank or on a prior work product counts as unsupported until traced to an unaltered primary document or a reproducible system value. The second rule is procedural and equally decisive: run it in a fresh session, because an agent that drafted the material carries its own conclusions in context and will confirm them, which is precisely the failure the audit exists to catch. Author and addressee are treated as part of every claim, since a quote attributed to the wrong person or a letter said to have been addressed to the reader when it went to someone else is a Blocker rather than a detail.

    Six error classes are hunted rather than awaited, each drawn from an observed failure: claims about one's own earlier knowledge that the author's own outbox refutes; misattribution; drifting figures, for which a value history is built and the one currently valid value named; reference periods that cut across each other, where the project's period definition must be established rather than assumed; third-party system evidence held only as a screenshot or a link, since a link is not preservation; and asserted deadlines with no traceable source. Work is ordered by deadline — ready-to-send drafts with a running clock complete before the case file itself — so a slow audit cannot cost a dispatch date. The Prompt is read-only by construction: no edits, no wording suggestions, no dispatch, and "not found" is an explicit valid finding so a missing source cannot be papered over with an invented one.

    Built as a Prompt rather than a Skill because it is a deliberately invoked workflow with a fixed procedure and a single artefact, the direct analogue of peer-review.prompt.md, and because a Skill covering claim-to-source verification would overlap citation-integrity and degrade auto-selection for both. It orchestrates citation-integrity, devils-advocate-review, and the severity labels of code-review-and-quality instead of restating them. Folder roles are derived from the Memory Bank routing table at run time rather than hard-coded, so the Prompt carries no project-specific paths, route names, or facts and applies to any matter that keeps primary sources apart from its own work products. Prompt count 10 → 11.

  • New Skill gilb-requirements-engineering (2026-08-04). Nothing in the library covered Tom and Kai Gilb's method, and the nearest neighbour was not a substitute: grill-me is Brooks-derived and deliberately qualitative, so "the portal must be significantly faster" survives its twelve-category interview intact and reaches the Design Concept as prose. The new Skill is the counterpart that refuses it. Its single rule is that a quality requirement without a Scale, a Meter, and a numeric level is a wish with a noun in it, and the nine-step protocol enforces the consequences. Sort every sentence into function, quality, resource, design, or condition and evict the design, because a requirement that names a technology has pre-empted the decision it was supposed to inform. State the benchmarks Past, Record, and Trend before any target, since Past and Goal together define the 0–100 % span the Impact Estimation Table computes against, and a Goal written without a Past leaves the table uncomputable. Keep Fail and Survival separate from Goal, so a missed ambition stops reading like an incident and the signal survives. Every number carries a Source or an explicit <TBD>, because an invented benchmark is indistinguishable from a measured one six months later.

    Four references keep the body at 322 lines. planguage-keywords.md carries the keyword set, the [qualifier] syntax that collapses five copy-pasted requirements into one, and the eight specification errors that recur. impact-estimation.md carries the table arithmetic and the 0.0–1.0 credibility scale, whose whole point is that a 90 % impact at credibility 0.2 loses to a 40 % impact at credibility 0.7 — the method systematically prefers what is known to work over what would be spectacular if it worked, and it surfaces negative cells that prose comparisons almost never do. evo-planning.md carries the roughly 2 %-of-budget step size, the three tests that separate a step from a task, the backroom/frontroom split that prevents delivery theatre, and the estimate-versus-actual feedback teams drop — which is exactly what turns Evo back into ordinary incremental delivery. spec-quality-control.md carries the 300-word logical page, the roughly one-page-per-hour checking rate that makes sampling mandatory, the ÷0.3 detection-effectiveness correction whose omission understates density threefold, and the ≤ 1.0 majors-per-page exit criterion with the finding that a failing specification is returned to its author rather than reviewed harder. Built as a Skill rather than a Custom agent because the knowledge is portable across harnesses and auto-triggers from every agent, where a persona has to be selected and pins a model priority array. grill-me now cross-references it on both sides of the overlap audit, so elicitation hands off to quantification instead of competing with it. Skill count 43 → 44.

Changed

  • pandoc-docx-export now carries the shading that Word silently drops (2026-08-07). Exporting a letter whose block quotes hold the counterparty's questions verbatim produced a Word file in which those quotes were indistinguishable from the author's own prose, and the same happened to every inline code span. The cause is not a conversion failure — pandoc maps block quotes to the BlockText paragraph style and inline code to the VerbatimChar character style exactly as it should — but neither style carries a fill in the stock reference document, so the distinction the Markdown preview shows is lost without any warning. Recipe 3 gains a Grey Shading for Block Quotes and Inline Code section with the two w:shd patches, the fills that stay legible in greyscale print, the optional left bar that reproduces the preview's look, and a verification snippet that counts BlockText paragraphs and VerbatimChar runs in the produced file rather than trusting the eye — a style that fails to apply falls back to body text and looks like ordinary output.

    The placement of those two elements is the part worth writing down, and it is now gotcha #6: w:pPr and w:rPr are ordered sequences, so w:shd belongs after w:pBdr and before w:spacing in a paragraph, and after w:sz and before w:vertAlign in a run. Word can refuse the file when they sit elsewhere and reports it as "unreadable content" without naming the element, which sends the search to the wrong place entirely. The gotcha also names the trap in the obvious defence: parsing the patched styles.xml with [xml] before repacking catches malformed XML but not an order violation, because a document with its children in the wrong order is still well-formed. The check that does work is a headless LibreOffice conversion to PDF, which yields a file only if the DOCX opens, runs unattended, and is faster than launching Word. The workflow step for the reference document and the frontmatter triggers are updated to match.

  • subagent-dispatch now bans handing a re-performer the answer (2026-08-06). The Skill already forbade pre-judging a reviewer — no "do not flag", no "at most Minor" — but said nothing about the mirror-image leak, which is the easier one to break by accident: dispatching a re-performance (recompute this, re-derive that, check these figures independently) with the expected values already reachable in the brief. A reviewer that knows the target finds the target, so the recomputation stops being evidence and becomes a second opinion on a number it was shown. The new Never hand a re-performer the answer section states the containment rule and, more importantly, names the workaround that does not work: putting the values in a "sealed" section at the end of the same brief. The brief is delivered as one text and read as one text, so a heading that says "open only after computing" is a request, not a barrier — the expected values go in a separate file the reviewer opens as a deliberate act, after its own numbers exist. Three consequences follow. Every other leak is named by path, because Memory Bank files, changelogs, prior reports, and commit messages routinely carry the result the reviewer is supposed to reach. Disclosure is required, not perfection — the reviewer states in its report what it read and when, since contamination that is declared is still usable while contamination that is hidden is not. And the asymmetry is read correctly afterwards: where the values were known early, the agreements are weak because confirmation bias is not excluded, while the disagreements are stronger than usual because they were produced against a known target rather than towards one. Integrated into the frontmatter triggers (blind re-performance, independent recomputation), the when-to-use list, the anti-rationalization table, the red flags, and the verification close, so a delegated recomputation is not done until it can show it derived its own values first.

Fixed

  • Three Prompts still gated on the pre-Decision-0001 Memory Bank path and aborted on a correctly initialised repository (2026-08-05). Decision 0001 moved the Memory Bank to .memory-bank/, and preflight.instructions.md states that a gate on memory-bank/ is obsolete and must not be silently worked around. Three Prompts had never been migrated. In export-emails.prompt.md and sync-project-emails.prompt.md the stale path sat inside a hard ABORT gate — Test-Path 'memory-bank/projectbrief.md' throws on every repository that follows the current convention, so the failure mode was not a wrong path but a refusal to run at all, reported to the user as a missing Memory Bank. deadline-action-handoff.prompt.md carried four read and write targets that would have read nothing and appended a progress entry into a second, newly created folder. All thirteen occurrences across the three files now use .memory-bank/. Deliberately left alone: ubiquitous-language.instructions.md, whose applyTo lists both variants on purpose and labels the non-hidden one legacy; the prose uses of the term in postflight.instructions.md and definition-of-done.md, which are not paths; and every reference to the Skill named memory-bank, including its own folder and the tests that assert on it.

3.0.0 - 2026-08-01

Added

  • New Skill german-tax-research (2026-07-31). The tax-researcher agent had instructed itself to "load german-tax-research" since the day it was written, and no such Skill existed — every income tax session therefore ran on whatever the model happened to recall about the EStG and the AO. The Skill closes that dangling reference and carries the material that a multi-year Einspruchsverfahren produced. Three rules in it are the ones that repeatedly decided outcomes. Four proof types, judged separately: an invoice evidences the cost and nothing else, so payment, professional purpose or participation, and the absence of third-party reimbursement each need their own document, and a missing one is closed with a signed Eigenbeleg or the position is dropped — never carried silently into the letter. Reconcile against the transmitted return, not the workbook: a control table with transmitted, evidenced, difference, and treatment per position is the only instrument that catches an allocation key a spreadsheet quietly "improved" away from the one the tax office applied, a bank credit that is a net figure hiding a gross receipt plus a disbursement, and a byte-identical invoice claimed in two different years. Disclose every difference in the point it belongs to, including corrections against the taxpayer's own interest, because § 153 Abs. 1 AO obliges notification once an error is known and an examiner who sees a self-reported correction reads the rest of the package differently. The Skill also fixes the deadline arithmetic that is wrong in most self-prepared objections: notification is deemed on the fourth day after dispatch since the PostModG took effect on 1 January 2025, not the third, and both that day and the end of the one-month objection period shift over weekends and state public holidays under § 108 Abs. 3 AO. scripts/Get-SteuerFrist.ps1 computes it, selects the three- or four-day fiction from the dispatch date, and reports every shift it applied together with the holiday set of the state where the Finanzamt sits.

    Six references keep the body inside the progressive-disclosure budget: fristen-und-verfahren.md for notification, objection, AdV, estimation, surcharges, interest, limitation, and competence; vermietung-und-afa.md for Anlage V, the AfA rates including the 5 % declining balance of § 7 Abs. 5a EStG, the purchase-price split after BFH IX R 26/19, and the handling of a § 7i certificate as an outstanding Grundlagenbescheid; werbungskosten-und-abzuege.md; belegaufforderung-antwort.md with the end-to-end answer procedure, the residence and centre-of-life evidence order, and the dispatch channels that actually prove receipt; kennzahlen.md with amounts and filing deadlines per assessment period 2021 to 2026, including the § 36 EGAO extensions that make the 2020 to 2024 dates non-obvious; and vorlagen.md with the German letter templates. Cross-referenced with evidence-package-assembly, which builds the Anlage PDF this Skill decides the content of, and with german-legal-research, whose DO NOT USE FOR now names the new Skill for tax questions. Skill count 42 → 43.

  • New Skill evidence-package-assembly (2026-07-30). Assembling an Anlage for an authority repeatedly hit the same three problems and none of them had a home in the Skill library. First, headless Microsoft Edge writes no PDF and still returns exit code 0 unless it is launched with --headless=new, a private --user-data-dir, and awaited as a process, so a Markdown-to-PDF pipeline appears to succeed while producing nothing; the Skill states the guard and ships scripts/Build-EvidencePackage.ps1, which throws when the output is absent. Second, deciding which sheets may be dropped from a bank statement or an official document has a reliable criterion that is not "topical relevance": a sheet carrying the source's own Seite X von N numbering stays even when it shows nothing relevant, because its removal leaves a visible gap and invites the reproach of selective presentation, while a sheet outside that numbering — terms-and-conditions notices, technical trailer lines — can be dropped without trace. Third, redaction is the wrong default for statements, because breaking the running balance destroys more evidential value than the privacy gain is worth. scripts/merge-and-verify.py takes a JSON manifest, merges with pypdf, prints the page range of every part so the cover is filled from the build output rather than by hand, and reads the sources' own page numbering back out of the assembled file to prove no sequence was broken. The Skill also collects the pandoc gotchas that only surface at render time: --metadata title duplicating the H1, single newlines collapsing a header block, pipe-table column widths coming from the dash counts in the separator row, and 17. April 2022 at the start of a line parsing as an ordered list.

  • New subagent-dispatch Skill (2026-07-30). Skills/subagent-dispatch/SKILL.md closes the largest gap in the library: eleven Custom agents can delegate, and nothing documented how. Covers the model-tier table (transcription → cheapest, mechanical → cheap, integration → standard, design and broad final review → most capable, review scaled to diff risk, fix rounds one tier above the model that got stuck) with the rule that an omitted model inherits the controller's model, usually the most expensive available, and the counter-intuitive economics that turn count beats token price — the cheapest models take two to three times the turns on multi-step work, so a mid tier is the floor for reviewers and for any subagent working from prose. Defines the five-part dispatch (placement line, brief path, interfaces from earlier work, resolved ambiguity, report contract) and bans pasted session history, since everything pasted stays resident in the controller context and is re-read every turn. Requires artifacts to change hands as files rather than inline text, forbids pre-judging a reviewer ("do not flag", "at most Minor"), and mandates a task ledger because conversation memory does not survive compaction and a controller that loses its place re-dispatches completed work. Adds a four-status report protocol where a subagent's success claim is a claim and the diff is the evidence, and a five-round fix cap with model escalation at round four, scoped re-reviews, written adjudication at the cap, and escalation to the user for load-bearing findings. Skill count 40 → 41.

Changed

  • The Post-flight gate now bounds the progress.md append it mandates (2026-08-01). The near-limit warning added earlier the same day tells you a budget is about to break, but nothing stopped the growth. Step 2 of Instructions/postflight.instructions.md requires an appended milestone on every substantive turn and set no limit, so the mandated write is itself the thing that breached the budget — twice. The step now reads "append a dated line to progress.md … curating the oldest entries in the same edit when the file is at or near its line budget", which places the retention obligation at the exact moment of the append and in the one file every agent reads every turn. Two supporting changes: Skills/memory-bank/SKILL.md runs Test-MemoryBankHealth.ps1 after any Memory Bank edit rather than only before reporting initialization complete — the health check existed but was scoped to the one path that never grows the file — and Skills/sampler-build-debug/SKILL.md gained a Reproduce a CI-Only Failure section carrying the three things that cost the most time diagnosing this: derive the commit CI built from the GitVersion +n suffix (the default branch has usually moved on, so a green HEAD proves nothing), reproduce in a clean clone rather than the worktree, and the two known local-versus-CI divergences — gitignored files present only in a worktree, and Sampler falling back to version 0.0.1 when dotnet-gitversion is absent. That fallback also yields a test authoring rule: never hard-code a version sentinel that can equal 0.0.1, or the test passes in CI and fails locally, which is what trains a team to stop running the local gate.
  • skill-creator now classifies the baseline failure before prescribing a guidance form (2026-07-30). The behavioural-enforcement pattern — anti-rationalization table, red flags, evidence close — was prescribed for every Skill encoding "a discipline an agent tends to abandon", with no distinction between failure types. A new Match the form to the failure section makes the classification the first authoring step and maps each failure type to its instrument: a discipline failure (knows the rule, skips it under pressure) takes the prohibition triad; a shaping failure (complies, but the output has the wrong shape) takes a positive recipe that states what the output is, part by part; an omission takes a structural REQUIRED slot in the template; and condition-dependent behaviour takes a conditional keyed to an observable predicate. Prohibitions backfire on shaping failures — given a competing incentive an agent negotiates with "don't do X" and can produce more of the unwanted content than no guidance at all — so the enforcement section is now explicitly scoped to discipline failures. Two supporting rules: no nuance clauses ("don't do X unless it matters" reopens the negotiation; express a real exception as its own conditional), and exemption clauses do not scope ("this limit does not apply to code blocks" still suppresses code blocks; restructure so the rule cannot reach the exempt part). The authoring checklist gained a matching gate.
  • agent-evals gained a wording micro-test loop with a mandatory no-guidance control (2026-07-30). The skill measured behaviour only through full capability and regression sets, which are too slow and expensive to iterate a single sentence against. The new Micro-test the wording first section adds the cheap inner loop: one fresh-context sample per call with the guidance in its realistic surrounding context rather than in isolation, five or more repetitions per variant, and every flagged match read by hand because template echoes and quoted counter-examples register as false hits. Two rules are new to the repository. A no-guidance control arm is mandatory, and it carries a stop condition — if the control does not exhibit the failure there is nothing to fix, so the guidance is not written at all, which prevents spending tokens teaching a model a problem it did not have. And variance is a metric: five different interpretations across five repetitions means the wording is not binding, so the form is tightened before more words are added.
  • debugging-and-error-recovery gained boundary instrumentation and a three-fix stop condition (2026-07-30). The reproduce → localize → reduce → fix → guard loop had no attempt counter and no guidance for failures that cross component boundaries — the common shape in the DSC, MECM, and AutomatedLab stacks this repository targets. Instrument the boundaries in a layered system requires diagnostic output at every boundary in one pass, capturing what enters, what leaves, and whether environment and configuration propagated, so one run localises the fault to a layer instead of one round trip per guess. Three failed fixes means the design, not the fix makes the third failure a stop condition rather than a cue for a fourth attempt, and names the signature that distinguishes a wrong design from a wrong hypothesis: each fix surfaces a new symptom elsewhere, each fix reaches further than the last, and making the fix correct would require "a big refactor first". At that point the design goes in front of the user. Two anti-rationalization rows and two red flags cover both additions.

Fixed

  • The release pipeline published to the Gallery without creating the release tag, which froze the next version (2026-08-01). Run 30689416495 failed with 409 (A package with id 'CopilotAtelier' and version '3.0.0-preview0001' already exists and cannot be modified.). The version is not computed from the commit count: GitVersion.yml runs in ContinuousDelivery mode, where the pre-release number is anchored on the last git tag and advances by one for each new tag — which is why the sibling ShellPilot repository, with an identical configuration, carries v0.2.0-preview0001 through v0.2.0-preview0008 and has never collided. Publish_Release_To_GitHub creates that tag as part of the GitHub release, and in this repository it is skipped on every run: its condition is -if ($GitHubToken -and (Get-Module -Name PowerShellForGitHub -ListAvailable)), PowerShellForGitHub resolves into output/RequiredModules as a dependency of Sampler.GitHubTasks, so the empty half is GitHubToken — the repository secret was never added. Publish_Module_To_gallery has its own token and ran anyway, so the module shipped untagged and every later build recomputed the version it had already published. Proven in a throwaway clone: with the unchanged configuration, adding the missing v3.0.0-preview0001 tag makes the next commit compute 3.0.0-preview0002. The pipeline now runs a Verify Release Secrets step before Publish Release that fails the deploy job when either secret is absent, so a missing token stops the release instead of shipping an untaggable one, and tests/Workflows.Tests.ps1 asserts that the guard precedes the publish step. The GitHubToken secret itself still has to be added to the repository.

  • The GitHub release was rejected because the changelog's [Unreleased] section had grown past the API's body limit (2026-08-01). With the token in place, run 30702468950 got as far as creating v3.0.0-preview0002 and then failed with 422 Validation Failed — body is too long (maximum is 125000 characters). Sampler builds the release body from (Get-ChangelogData).Unreleased.RawData, and that section stood at 143,697 characters: version 2.0.0 shipped to the Gallery on 2026-07-29 but was never recorded, because Create_ChangeLog_GitHub_PR needs the same missing GitHubToken — so the identical secret gap that suppressed the tag also let [Unreleased] accumulate three releases' worth of entries. Everything that [Unreleased] held at the v2.0.0 tag now sits in a 2.0.0 section dated 2026-07-29 with its own compare link, leaving 15,441 characters of genuinely unreleased work. tests/QA/module.tests.ps1 now fails the build when [Unreleased] passes 100,000 characters, so the limit is hit by a local test run rather than by a half-finished release that has already created its tag.

  • The Memory Bank health check now warns before a line budget is breached, instead of only failing after (2026-08-01). CI run 30568587317 failed one test, MemoryBankHealth.Tests.ps1 → "passes the repository canonical files and compactness budgets", because .memory-bank/progress.md had reached 220 lines against its 200-line budget. This is the second occurrence of the same failure mode: the file is append-only — the shared Post-flight gate adds a milestone on every substantive turn — while the budget is a hard error with no signal on the way up, so the breach is always discovered by a red build rather than by the author who caused it. The budget is unchanged, because the compactness contract is what the routed loading mode is built on. Test-MemoryBankHealth.ps1 now raises a LineBudgetNearLimit warning at 90 % of any line budget, the repository health test prints those warnings so a passing run still names the file about to breach, and Skills/memory-bank/SKILL.md states that the warning is a curation task for the same turn. Applying the documented retention to progress.md restored 62 lines of headroom; techContext.md at 194 of 200 lines is now flagged and tracked as open work.

  • german-tax-research priced Aussetzungszinsen at a third of the statutory rate (2026-08-01). Surfaced by a statutory audit of the new Skill against the consolidated text in force on 1 August 2026. references/fristen-und-verfahren.md, references/kennzahlen.md, and references/vorlagen.md all stated 0.15 % per month for § 237 AO. The reduced rate of § 238 Abs. 1a AO applies by its own wording only "in den Fällen des § 233a", and § 237 AO carries no rate of its own, so AdV interest runs at § 238 Abs. 1 S. 1 AO0.5 % per month, 6 % per year. A Skill whose purpose is deciding whether to seek suspension of enforcement was therefore understating the downside of losing by a factor of more than three. Independently confirmed by BFH, Vorlagebeschluss v. 08.05.2024 – VIII R 9/23, which describes the AdV rate as "einhalb Prozent pro Monat … gem. § 237 i. V. m. § 238 Abs. 1 Satz 1 AO" and refers it to the BVerfG as unconstitutional for 1 January 2019 to 15 April 2021 — so the corrected text now also carries the practical consequence: object and apply for the procedure to rest under § 363 Abs. 2 S. 2 AO rather than paying.

  • The § 152 and § 233a month counts were wrong for exactly the assessment periods the Skill covers (2026-08-01). The reference stated the flat 14-month trigger for a mandatory Verspätungszuschlag and the flat 15-month Karenzzeit for interest. Art. 97 § 36 Abs. 3 Nr. 5 and Nr. 7 EGAO replace both for the assessment periods 2020 to 2024: 20/20/19/17/16 months for the surcharge and 21/21/20/18/17 months for the interest run. A case on VZ 2021 to 2024 — the years a live objection is about — would have produced a surcharge that is not owed and an interest period that starts up to six months too early. Both tables now carry the per-VZ override, and SKILL.md gained the matching anti-pattern alongside a second one for the AdV rate.

  • Two further statutory drifts in german-tax-research (2026-08-01). § 238 Abs. 1c AO requires the interest rate to be evaluated at least every two years, first by 1 January 2024 — the reference said every three years. And § 10 Abs. 1 Nr. 5 EStG has allowed 80 % of childcare costs up to 4,800 euro per child since VZ 2025 (JStG 2024); the reference still stated the pre-2025 two-thirds-up-to-4,000 rule without a year key. The audit confirmed the rest of the Skill against primary sources: the four-day Bekanntgabefiktion since the PostModG, the whole § 149/§ 36 EGAO filing-deadline table cell by cell, the Steueränderungsgesetz 2025 figures (0.38 euro from the first kilometre, the 2,000 euro foreign accommodation ceiling under § 9 Abs. 1 S. 3 Nr. 5 EStG, 28/14 euro Verpflegungspauschalen unchanged for 2026), and the case-law citations BFH IX R 19/24 of 14 January 2025, BFH IX R 26/19, BFH VI R 75/14, and BFH GrS 1/06. Get-SteuerFrist.ps1 reproduces its own documented worked example.

2.0.0 - 2026-07-29

Added

  • A canonical GitHub Actions template for Sampler repositories (2026-07-29). Skills/sampler-framework/references/ci-cd-pipelines.md advertised "Azure Pipelines and GitHub Actions templates" but only ever contained the Azure Pipelines one, so every Actions pipeline was written from scratch and drifted — which is exactly how this repository's own ci.yml diverged from its siblings. The reference now carries the full .github/workflows/ci.yml template, a second-PowerShell-edition matrix variant, and an Azure Pipelines to GitHub Actions translation table covering the constructs with no direct equivalent: ##vso[task.setvariable] becomes $GITHUB_OUTPUT plus job outputs, ##vso[build.updatebuildnumber] has no equivalent at all, pwsh: true|false becomes a defaults.run shell because a step's shell key accepts no context, and the org guard becomes github.repository_owner. Instructions/sampler.instructions.md gained a CI/CD Rules section and now applies to azure-pipelines.yml and .github/workflows/*.yml, so the template and its non-negotiables load automatically whenever a Sampler pipeline is edited.

  • The CI workflow pins the current GitHub Actions majors (2026-07-29). Every job warned that actions/checkout@v4, upload-artifact@v4, and download-artifact@v4 target the deprecated Node 20 runtime. Bumped to checkout@v7, upload-artifact@v7, and download-artifact@v8 — the three actions do not share a major number, and the release where each stopped defaulting to Node 20 differs (checkout v5, upload-artifact v6, download-artifact v7), so a uniform bump to any single number would have left two of them deprecated. The documented breaking changes do not apply here: checkout@v7 only restricts fork checkouts under pull_request_target and workflow_run, and download-artifact@v5 only changed the output path for downloads by artifact ID, not by name. The skill template carries the same pins plus a per-action version table.

  • GitHub Actions CI (2026-07-29). New .github/workflows/ci.yml — the repository had no CI at all. It follows the same build/test/deploy shape as the other Sampler repositories so one pipeline can be reasoned about everywhere: a Package Module job computes the version with GitVersion, exports every GitVersion property as a step output, stamps FullSemVer into the job summary, and uploads output/ as the build artifact; the downstream job names carry that version, which is the closest GitHub Actions gets to Azure DevOps' run renaming. Test reuses the artifact on Linux, macOS, and Windows, all on PowerShell 7, with the non-Windows legs restricted to -PesterTag @('Unit','QA') because the repository also tests Windows-only Customizations. Deploy Module publishes the GitHub release and the Gallery package from main or a v* tag, is fenced to the upstream owner, and needs the GitHubToken and GalleryApiToken repository secrets. Pushes that only touch CHANGELOG.md are ignored so the release commit does not retrigger the pipeline.

    The matrix originally carried a fourth leg that repeated the Windows run on Windows PowerShell 5.1, and that leg was dropped on 2026-07-30 (run 30526269252). It failed in Invoke_Pester_Tests_v5 with The file '…\CopilotAtelier.psd1' could not be parsed as a PowerShell Data File, before a single test ran. Create_Changelog_Release_Output stamps the changelog release section into PrivateData.PSData.ReleaseNotes as a single-quoted string and writes the manifest as UTF-8 without a byte order mark; Windows PowerShell 5.1 decodes a BOM-less file with the ANSI code page, so the UTF-8 bytes of (E2 86 92) become â†' — and that trailing ' is a string delimiter to the tokenizer, which terminates the release notes early and breaks the parse of the whole manifest. Verified locally: the same built manifest imports cleanly on PowerShell 7 and fails to parse the moment its bytes are decoded as code page 1252. Rather than force a BOM onto the built manifest to keep an interpreter green that nobody runs this module on, the leg is gone.

  • A module test suite (2026-07-29). tests/QA/module.tests.ps1 enforces the changelog parse, the exported command surface, the presence and non-emptiness of all six shipped customization directories inside the built module, a unit test per exported command, zero PSScriptAnalyzer findings per command, and complete comment-based help including a documented example and every parameter. tests/Unit/ covers the three public commands and the two load-bearing private helpers (ConvertFrom-Jsonc, Get-CopilotAtelierPath) against a sandboxed profile, including the legacy-location cleanup, the keybinding merge, the discovery links, and the version-comparison paths.

  • A README Quick Start (2026-07-29). Installation previously sat 150 lines into the README, behind the whole skill catalogue, so a first-time reader met the inventory before the two commands that get them running. The new Quick Start leads with prerequisites, Install-Module plus Install-CopilotAtelier, the restart, and Update-CopilotAtelier, and states plainly that the Gallery package is not published yet. The Gallery section gained the day-two loop it was missing: the useful switches, a Get-Help pointer, sample Get-CopilotAtelierVersion output with what IsCurrent : False means, and what a second machine still has to do for itself.

  • Hooks: a fifth Customization type with deterministic guardrails (2026-07-28). New Hooks/ folder deployed by Setup to ~/.copilot/hooks, the well-known user-level hook location read by VS Code, the GitHub Copilot CLI, and Claude Code. Until now every house rule — never push, probe for the Memory Bank, open with a UTC timestamp — lived only in auto-applied Instructions, so enforcement depended on the model choosing to comply. Two hooks close that gap. scripts/Block-RemoteMutation.ps1 runs on PreToolUse, inspects terminal tool calls only, and exits 2 (the VS Code blocking contract) for a command that pushes to a git remote, passes --no-verify, runs git reset --hard, force-cleans untracked files, or mutates a pull request, issue, release, or repository through the GitHub CLI; COPILOT_ATELIER_ALLOW_REMOTE=1 is the documented per-command override for a push the user authorized in the current turn. Non-terminal tools exit 0 immediately, so editing a document that merely mentions git push is never blocked, and an unreadable payload exits 1 (non-blocking warning) rather than bricking every tool call. scripts/Add-SessionContext.ps1 runs on SessionStart, probes the filesystem for .memory-bank/index.md, and injects an authoritative present-or-absent statement plus the UTC timestamp — removing the recurring failure where an agent concludes "no Memory Bank" from a workspace summary that omits dotfile folders. Commands declare a POSIX default and a windows override, so one configuration works on all three platforms. See Hooks/README.md and Decision 16.

  • Agent plugin manifest (2026-07-28). New root plugin.json makes the library installable with Chat: Install Plugin From Source from a Git URL, discoverable under @agentPlugins in the Extensions view, and updated automatically — in VS Code, the GitHub Copilot CLI, and Claude Code from one repository. It declares Agents/ and Skills/; Instructions and hooks are not part of the plugin format and remain the Setup script's job, so the two distribution paths are complementary rather than alternatives.

  • compatibility on 25 environment-bound Skills (2026-07-28). The Agent Skills specification defines an optional compatibility field for environment requirements and nothing in the library used it — so a Linux or Claude Code user could load outlook-email-export or windows-gui-screenshot-capture with no signal that they are Windows-and-COM only. Added to the Windows-bound Skills (automatedlab-deployment, automatedlab-proxmox, create-outlook-draft, dsc-troubleshooting, mecm-dsc-deployment, outlook-calendar-export, outlook-email-export, send-outlook-email, whisper-pyannote-transcription, windows-gui-screenshot-capture, winrm-troubleshooting) and to the Skills with hard toolchain dependencies (authenticated-web-extraction, datum-configuration, docx-to-markdown, marp-slide-overflow, mcp-builder, microsoft-todo-tasks, pandoc-docx-export, pdf-to-markdown, pester-patterns, pswritehtml-reporting, sampler-build-debug, sampler-framework, sampler-migration, xlsx-to-markdown).

  • Forked contexts for the two Skills that ingest untrusted external content (2026-07-28). social-signal-sweep and citation-integrity declare context: fork, so each runs in a dedicated subagent and returns only its final report. Besides keeping the main conversation's context clean, this keeps raw fetched pages — an untrusted, injection-capable input — out of the parent agent's context. Setup writes github.copilot.chat.skillTool.enabled to enable the feature.

  • Two regression suites (2026-07-28). tests/Hooks.Tests.ps1 runs both hook scripts through a child process exactly the way VS Code invokes them — payload on standard input — covering eight blocked commands, five allowed commands, the non-terminal-tool case, the override path, the unreadable-payload path, both Memory Bank states, the SessionStart output contract, and the hook configuration itself. tests/SkillFrontmatter.Tests.ps1 enforces the Agent Skills specification across every Skill: name matches the parent directory and is kebab-case within 64 characters, description within 1024, compatibility within 500 and present on every environment-bound Skill, valid context, and a body budget with a documented, non-growing baseline of the ten Skills currently over 500 lines. Repository total: 250 passing tests.

  • Add deterministic Memory Bank health and routing evaluations (2026-07-24). Add 25 independently audited real-task cases with zero-labeled-critical-miss, history-isolation, Full-read fallback, and at-least-50-percent context-reduction gates; extract 15 metadata-bearing Decision records; add optional explicitly routed Memory Bank topics; and add Test-MemoryBankHealth.ps1 checks for seven required version-controlled files, optional local prompt history, provenance, freshness, retention, and compactness budgets.

  • New memory-bank Skill for safe project-memory initialization (2026-07-23). Add seven required version-controlled files (index, projectbrief, productContext, activeContext, techContext, progress, systemPatterns) plus local promptHistory, evidence-based minimal templates, additive role-specific extensions, retention rules, secret-handling safeguards, and capability/decoy eval cases. The bundled PowerShell initializer writes LF-only UTF-8 without BOM, supports -WhatIf, reports Created/Preserved/Planned outcomes, is idempotent, and preserves every existing file byte-for-byte. Pre-flight initializes only missing files before durable repository writes and does nothing for read-only or transient tasks.

  • Memory Bank Glossary and Ubiquitous Language (2026-07-21). Add .memory-bank/glossary.md with 20 Canonical term entries covering the four Customization types, the Canonical target and Discovery link model, the Setup script, Pre-flight and Post-flight, turn classification, Agent-to-agent handoff, Session handoff, Acceptance criteria, and the Definition of Done. Every row uses the required Term | Means | Don't say shape. All 40 forbidden phrases are domain-qualified and have zero existing matches outside the Glossary; literal filenames, paths, commands, external API fields, and quoted historical text retain their exact spelling.

  • New windows-gui-screenshot-capture skill (2026-07-17). Packages the reusable capability proven in the D:\guitest GUI-screenshot-docs proof-of-concept: reliably capture screenshots of a Windows desktop GUI programmatically and assemble them into a screenshot-embedded Markdown user manual, generalised to real apps rather than tied to the POC. Skills/windows-gui-screenshot-capture/SKILL.md documents, as a six-step frame, the capture-API-per-rendering-engine matrix (WPF RenderTargetBitmap; WinForms Control.DrawToBitmap sized to Form.Size, not ClientSize; Win32/GDI PrintWindow with PW_RENDERFULLCONTENT; WebView2 CoreWebView2.CapturePreviewAsync; Avalonia headless CaptureRenderedFrame; WinUI 3 RenderTargetBitmap.RenderAsync / Windows.Graphics.Capture), the GPU-composited-returns-black rule (PrintWindow / BitBlt return black for WebView2 / WinUI 3 / UWP — use the framework API or Windows.Graphics.Capture), the self-capturing -CaptureDir / --capture scene mode that steps through named UI states on a dispatcher timer and self-terminates so it runs unattended without blocking the terminal, native MessageBox capture (locate the dialog by window class #32770 + title via FindWindow and capture-then-dismiss with a timer that keeps firing through the modal loop — never the foreground window), and the STA / WinForms-DPI-unaware / WPF-SizeToContent / Avalonia-headless-font gotchas. Dogfoods the behavioural-enforcement pattern (anti-rationalization table + red-flags list + verification/evidence close). Ships a one-level-deep reference (references/engine-recipes.md — six per-engine snippets with a ## Contents TOC, each linking to the POC file for the full implementation), the POC's native-dialog helper copied verbatim with its origin noted (scripts/DialogCapture.ps1FindWindow + PrintWindow + PostMessage, 0 AST parse errors), and notes-evals.md (6 intended-trigger + 3 decoy eval prompts). Description 955/1024 chars, third-person with USE FOR: / DO NOT USE FOR: (fences cross-platform/mobile screenshots, screen-video capture, and generic PowerShell GUI tutorials); body 209 lines (≤ 500); references one level deep; folder name == name:; markdownlint-cli2 reports 0 issues across the 3 Markdown files. Registered in the README Available Skills table and the techContext.md Skills inventory. Skill count 38 → 39.

  • New automatedlab-proxmox troubleshooting skill (2026-07-16). Correlates controller, Proxmox/QEMU, guest-agent, Windows setup, and required-protocol evidence; separates AutomatedLab's deploy-time Sysprep/AppX mismatch from an unusable pre-generalized source template; adds a non-destructive throwaway-clone ImageState/Panther probe, specialized non-sysprepped golden-image remediation, and the external-scope Proxmox task-wait constraint; and retains QEMU command completion, task-warning, restart-scoped WinRM, flags-metadata, stale-module, fallback-build, regression-test, and controlled-live-verification guidance. Ships three direct references and ten real-incident eval cases. Skill count 37 → 38.

  • Three domain-neutral engineering-discipline skills: test-driven-development, debugging-and-error-recovery, code-review-and-quality (2026-07-09). Original, PowerShell/DSC-flavoured workflows for the disciplines an agent most often skips, each dogfooding the new behavioural-enforcement pattern (anti-rationalization table + red-flags list + evidence/verification close) and fenced with DO NOT USE FOR against the repo's existing domain playbooks. Skills/test-driven-development/SKILL.md (116 lines) — red-green-refactor, a failing Pester test before the code, the test pyramid, DAMP-over-DRY, bug-fix-as-test-first, and characterization tests for legacy code; delegates mocking recipes to pester-patterns and failure diagnosis to sampler-build-debug. Skills/debugging-and-error-recovery/SKILL.md (96 lines) — a reproduce → localize → reduce → fix-root-cause → guard loop, stop-the-line, do-not-swallow-errors, and flaky-test discipline with PowerShell tactics; delegates DSC/WinRM/Sampler specifics to dsc-troubleshooting / winrm-troubleshooting / sampler-build-debug. Skills/code-review-and-quality/SKILL.md (98 lines) — a five-axis review (design, correctness, complexity, tests, clarity), severity labels (Blocker/Major/Minor/Nit), change sizing, and an author self-review gate against the Definition of Done; delegates the security review to the code-review prompt / agent-security-review and adversarial critique to devils-advocate-review. All three link to Reference/definition-of-done.md; descriptions 1021 / 998 / 976 chars (≤ 1024); bodies ≤ 500 lines; LF + UTF-8 no BOM; markdownlint clean. Registered in the README Available Skills table and the techContext.md Skills inventory. Skill count 34 → 37. Each skill ships a notes-evals.md trigger set (four Claude-A/Claude-B scenarios).

  • New reference Reference/definition-of-done.md — the project-wide standing quality bar (2026-07-09). A concise, non-auto-attached reference stating the single bar every change clears before it counts as done, explicitly contrasted with per-task acceptance criteria (a change ships only when it satisfies both). Organises the bar into six cross-linked groups: Process (the per-turn pre-flight / post-flight contract), Verification (proof not "looks right", by file type — PowerShell AST parse + PSScriptAnalyzer + approved verbs + Pester; Markdown markdownlint-cli2 0 violations; YAML/JSON parse + schema; C# build + analyzers), Authored content (Skills/Instructions/Agents/Prompts caps and rules), Repository hygiene (Memory Bank + CHANGELOG + local ai/<slug> commit + never-push), Security (no plaintext secrets, [PSCredential]/SecureString, lethal-trifecta + OWASP LLM Top 10 screen for agents/MCP), and Ubiquitous Language. Each gate links to the instruction or skill that owns it. Includes a "when a criterion does not apply" rule (skip only with a stated reason; silent skipping is a process violation) and a "point, don't inline" usage note for skills/agents/prompts that reference the shared bar. LF line endings, UTF-8 no BOM per .gitattributes; 70 lines; markdownlint clean. Reference-doc count 3 → 4. postflight.instructions.md links to it from its scope note as the full standing bar it partially enforces.

  • New skill Skills/long-running-job-monitor/SKILL.md — running and monitoring long jobs with timestamped, verifiable status (2026-07-07). Governs how the agent runs, monitors, and reports on long-running live tests, integration suites, installers, and deployments (many minutes to tens of minutes, output often buffered). Six load-bearing techniques: (1) instrument the job into a self-timestamping log (START, per-phase lines, silent-phase heartbeats, a unique <JOB>-DONE/-FAILED marker, tee'd to $env:TEMP) so the log — not the agent's memory — is the source of truth; (2) run it so it survives and self-notifies (sync-no-timeout preferred, async only for indefinite processes) and never Start-Sleep in the agent's own foreground command or hand-roll a poll loop; (3) verify progress out of band and read-only against the target (hypervisor/cloud API, QEMU guest-agent, SSH/WinRM/CIM, DB query, HTTP health, kubectl, az/aws); (4) an optional background monitor sidecar samples every 300 s into a .status file; (5) a stuck-vs-working heuristic (WORKING/STALLED/DONE/FAILED) with strict timestamps and a phase-sized threshold; (6) completion + cleanup that verifies the real end-state and tears down throwaway resources. Ships a reference (references/out-of-band-verification.md — per-domain read-only probes with a change-detector pattern), a parameterized sidecar (scripts/Start-JobMonitor.ps1 — clean under PSScriptAnalyzer), and notes-evals.md with regression scenarios for deployment status, stalls, buffered output, process death, and remote channel loss. Includes a Remote jobs (SSH / WinRM / PowerShell Direct) subsection — run techniques 1–3 on the remote side, keep the instrumented log and liveness probe remote, verify via an independent control plane, and treat a dropped control channel as reconnect-and-recheck rather than FAILED (channel death is not job death). Extends the new systemPatterns.md Decision 10 rather than duplicating the sync/async basics. Description 1006 chars, body 173 lines. Skill count: 32 → 33.

  • New skill Skills/pswritehtml-reporting/SKILL.md — generate interactive HTML reports from PowerShell (2026-07-07). On-demand skill for turning PowerShell objects into self-contained, interactive HTML with the MIT-licensed, dependency-free, cross-platform PSWriteHTML module. Documents the New-HTML { } container model (offline-inlined assets by default, -Online for CDN links; returns a string when -FilePath is omitted, otherwise writes the file and -ShowHTML opens it) and seven recipes: New-HTMLTable (DataTables filtering/paging/-SearchBuilder/-Buttons), New-HTMLTableCondition conditional formatting (-Inline for email), New-HTMLSection/New-HTMLPanel/New-HTMLTab dashboard layout, New-HTMLChart with New-ChartBar/Line/Pie/Donut, New-HTMLDiagram with New-DiagramNode -To/New-DiagramLink, Out-HtmlView as a cross-platform Out-GridView alternative, and an HTML email-body recipe that generates the body and delegates sending to the send-outlook-email skill. Includes a New-HTMLTableOption -DataStore JavaScript large-dataset tuning note, a gotchas list (nothing renders outside New-HTML, duplicate diagram-node labels merge, JavaScript vs .NET date tokens, offline file size), and a Test-Path/Select-String output-verification step. A > [!WARNING] block steers users away from the module's plaintext -PasswordFromFile SMTP pattern toward a [PSCredential] or Mailozaurr Send-EmailMessage OAuth2, per the repo's PowerShell security rules. PSWriteHTML is not installed locally, so every command name was verified against Evotec's published examples before authoring. DO NOT USE FOR fences delegate sending to send-outlook-email, Markdown-to-Outlook drafts to create-outlook-draft, slide decks to marp-slide-overflow, Word/PDF export to pandoc-docx-export, and document conversion to the *-to-markdown skills. Description 1005 chars, body 192 lines. Skill count: 32 → 33.

  • Repo markdownlint config codifying the markdown house style (2026-07-02). New .markdownlint.jsonc makes the repo lint clean and deterministic in the editor, the markdownlint-cli2 CLI, and CI without reformatting content. The repo predates any lint config and deliberately uses long single-line entries, compact pipe tables (|---|---|, 1200+ across 74 files), bare code fences for diagrams/trees/output, and **bold** lead-ins; the matching stylistic rules (MD013, MD060, MD040, MD012/022/028/031/032/058, MD024, MD026, MD029, MD033, MD034, MD036, MD049, MD001, MD009, MD014, MD038) are disabled while MD047 (single trailing newline) is kept and auto-fixed repo-wide. Bundled fixes: the four editor-visible bare fences gained a text language (README.md folder tree; Reference/copilot-cli-model-routing.md flowchart + two task() blocks), and five files gained a trailing newline. Genuine correctness issues the linter surfaced are disabled-but-flagged in the config for a follow-up pass: MD051 link fragments (×5), MD056 table-column count (×1), MD041 (×1), MD038 (×4).

  • Two new skills for agentic-AI security and evaluation, plus a portable AGENTS.md (2026-07-02). (1) Skills/agent-security-review/SKILL.md — a reusable, on-demand checklist for reviewing agentic and LLM-backed systems: the lethal-trifecta test (private data × untrusted content × outbound channel, remediated by breaking a leg rather than filtering it), OWASP Top 10 for LLM Applications (2025) quick checks (LLM01 prompt injection, LLM02 sensitive-info disclosure, LLM05 improper output handling, LLM06 excessive agency, LLM08 vector/embedding weaknesses, with LLM03/04/07/09/10 screened), an explicit "prompt injection via tool output" check (tool results, fetched pages, READMEs, issue text are untrusted input), a containment-first checklist (sandbox, default-deny egress allow-list, scoped least-privilege identity, human-in-the-loop gated against ~93% approval fatigue), and an MCP / tool-permission review (confused-deputy risk, "audited connector ≠ audited data"). Description 921 chars, body 113 lines. Loaded by the security-reviewer and software-engineer agents. (2) Skills/agent-evals/SKILL.md — how to build evals for your own skills/prompts/agents: capability vs regression eval sets, grader types (deterministic / LLM-as-judge / human), pass@k vs pass^k for non-deterministic runs, eval-driven development, and a minimal scripts/run-evals.ps1 harness (deterministic graders; capability gated on pass@k, regression on pass^k) plus assets/evals.sample.json; "start from 20–50 real failures." Description 926 chars, body 114 lines. (3) New repo-root AGENTS.md — tool-neutral house rules (pre/post-flight contract, never push, approved-verb PowerShell, Pester-first, authoring rules, current model) that make the toolkit portable across Copilot, Claude Code, Codex, Cursor, and other AGENTS.md-aware harnesses. Skill count: 30 → 32.

  • Brand identity assets and a theme-aware logo in the READMEs (2026-06-11). New assets/ folder holds four transparent, auto-cropped Format32bppArgb PNGs derived from the Copilot Atelier design board: CA-logo-on-light.png / CA-logo-on-dark.png (compass-in-"D" icon + wordmark; the navy "Copilot" is lifted to near-white #EAF1F8 for the dark variant while the teal "Atelier" accent is preserved) and CA-glyph-on-light.png / CA-glyph-on-dark.png (icon-only, darker teal for light backgrounds and brighter teal for dark). The root README.md header floats the logo left through a prefers-color-scheme <picture> element so the intro wraps to its right, with <br clear="left"> restoring flow, and rebrands the H1 from "Copilot Customization via OneDrive" to "Copilot Atelier" with a one-line descriptor; Agents/README.md gains a right-floated glyph corner mark. All inline-HTML blocks are fenced with markdownlint-disable MD033 MD041 / enable comments. New .gitattributes (the repo previously had none) normalises line endings and marks image and archive types binary per Instructions/git.instructions.md. The source design-board PNGs are not shipped; transparency was produced by colour-to-alpha against the off-white flatten and verified by compositing every variant on #0d1117 and #ffffff.

  • New skill Skills/social-signal-sweep/SKILL.md — recency-bounded social lead generation for the research pipeline (2026-06-08). Surveys what people publicly say about a topic over a bounded recent window (default 30 days) across GitHub (native GitHub tools), Hacker News (Algolia API), Reddit (public JSON), and Stack Overflow (Stack Exchange API), plus a lower-confidence browser-only tier for YouTube and X via openSimpleBrowser. Returns a tier-8 lead sheet — platform, date, engagement signal, link, and an explicit "what to verify" per row — under a hard leads-only, never citable contract: engagement measures attention, not accuracy, and no sweep result may raise a claim above Weak confidence on its own. Carries no bundled engine, no API keys, and no scraping cookies (uses only web/fetch, the GitHub tools, and openSimpleBrowser), in deliberate contrast to its inspiration. Documents per-platform no-auth recipes, a one-shot epoch/ISO cutoff for time-bounding, keyword-trap reframing (demographic-shopping phrasing, colliding bare numbers, tutorial phrasing, generic single nouns), and 403/429 browser fallbacks. Wired into Agents/research-analyst.agent.md as a new SOURCE-phase Recency sweep for lead generation bullet that keeps every lead at Weak/Speculation until VERIFY triangulates it, and cross-links citation-integrity and authenticated-web-extraction. Description 1000 chars, body 221 lines. Skill count: 29 → 30.

  • New prompt Prompts/session-handoff.prompt.md and session-handoff storage convention (2026-05-27). On-demand /session-handoff prompt that produces a compact, pointer-based handoff document so a fresh agent in a new session can continue work without re-investigating context (context saturation, model switch, machine handover, teammate handover). agent: agent so any active persona can produce one. Writes to .memory-bank/session/handoff-<UTC>.md where <UTC> is YYYY-MM-DDTHHmmZ (ISO-8601 compact); excluded from version control via new repo-root .gitignore (also covers the older deadline-handoff-*.md payload produced by sync-project-emails Phase 7a). Per-session ephemera stays out of project history — progress.md and CHANGELOG.md remain the canonical record. The new .memory-bank/session/README.md (tracked) documents the folder's purpose, lifecycle, and how the next session consumes a handoff. Required document sections: Header (UTC, pattern: closing / forward / return, source agent, model, branch, worktree, last SHA short, dirty files from git status --porcelain, parent-handoff path for return only), Mission (one paragraph; uses prompt arguments as focus if passed), State pointers (paths only — .memory-bank/activeContext.md, progress.md latest entry, session plan.md, CHANGELOG.md [Unreleased] line range, open todos, in-flight Results/ artifacts, branch-scoped spec files; never inline duplicated content), Suggested next agent (one name: from Agents/*.agent.md, or agent / ask, or a different harness/tool — Claude Code / Codex / Copilot CLI / Cursor — for cross-tool handoff), Suggested skills (loaded-this-session + pre-load-for-next), Open questions, Redaction note (API keys, refresh tokens, OAuth client secrets, passwords, connection strings, SAS URIs, PII, mailbox content from outlook-email-export / outlook-calendar-export, browser-profile data under %LOCALAPPDATA%\CareerAuthBrowser\). One handoff per invocation; never overwrites a prior file. Three explicit patterns shape document content: closing (this session ends, next session resumes same mission), forward (this session continues, child session takes a discovered out-of-scope sub-task), return (child session reports compressed learnings back to the parent that spawned it). Focus statement is mandatory: arguments verbatim → unambiguous derivation from activeContext.md + last user turn → otherwise Mission stays empty and "Next-session focus undefined" is the first Open question (no fabricated missions). New .memory-bank/systemPatterns.md Decision 8 disambiguates this from Decision 4 ("Agent-to-agent handoff" — the in-session UI transfer between custom agents in the same chat, declared via handoffs: in agent frontmatter). Same word, two problems. Prompt count: 9 → 10.

  • New skill Skills/grill-me/SKILL.md and new instruction Instructions/ubiquitous-language.instructions.md (2026-05-20). grill-me is an on-demand adversarial requirements-interview skill: refuses to write code, tests, or designs until the user answers 40–100 questions across twelve mandatory categories (Purpose & success criteria · Users & stakeholders · Inputs & outputs · Failure modes · Edge cases · Security & privacy · Performance & scale · Operational ownership · Rollback & reversibility · Observability · Non-goals · Open questions); one question or tight cluster at a time; emits a fixed-layout Design Concept document and waits for explicit SIGNED OFF before producing any other artefact. Push-back protocol: refuse the first override politely, comply on the second but log it verbatim in an Override log: section at the top of the Design Concept. Theoretical grounding: Brooks, The Design of Design (2010); independent rewrite inspired by but not derived from https://github.com/mattpocockuk/skills. Pairs with skill-creator, doc-coauthoring, and the new ubiquitous-language instruction. ubiquitous-language is a pattern-matched instruction (Evans, Domain-Driven Design, 2003) that activates whenever docs/glossary.md, glossary.md, or (.)memory-bank/glossary.md is present in the workspace; applyTo also covers **/*.md, **/*.ps1, **/*.py, **/*.cs, **/*.ts, **/*.js so the rules govern the artefacts the glossary itself governs. Glossary shape is a checked-in three-column table Term | Means | Don't say. Five enforcement rules: read the glossary before planning any change; use canonical terms only in code, comments, log messages, test names, variable names, documentation, and commit messages; never introduce a forbidden synonym (translate user input once and note the translation); propose adding a row when a needed concept is missing rather than inventing a synonym; flag drift without silently rewriting unrelated code. Out-of-scope: user-facing UI copy and third-party API field names. Both files are the repo-side anchors for slide 31 (Two Patterns for Context — Grill-Me + Ubiquitous Language) of the AgenticOperatingModel deck.

  • Two new skills and one new prompt adopted from review of Imbad0202/academic-research-skills (2026-05-20, CC BY-NC 4.0 source; all three files are independent rewrites with attribution noted in their ## Attribution sections — no source files were copied). (1) Skills/citation-integrity/SKILL.md — verify every external claim, quote, statistic, and reference against a fetched source. Six-class failure taxonomy: F1 fabricated reference, F2 plausible-but-wrong attribution, F3 identifier hallucination, F4 partial hallucination, F5 claim-not-supported (the dangerous one — source exists and is cited correctly but does not actually make the attributed claim), F6 anchorless claim. Three-layer anchor for every VERIFIED verdict (locator: page/section/paragraph; quote: ≤25 words verbatim; identifier: stable URL/DOI/ISBN+edition/file hash). Three verdicts only — VERIFIED / MISMATCH / NOT_FOUND; no gray zone, no silent skips (paywalls are NOT_FOUND with reason). Iron rule: no memory verification, ever. Cross-index triangulation (Crossref + OpenAlex / Semantic Scholar + publisher) required before declaring F1. (2) Skills/devils-advocate-review/SKILL.md — argue against a proposal, design, claim, or draft from a hostile-but-fair position with explicit anti-sycophancy guardrails. 1–5 rebuttal scoring rubric run before drafting the reply (5 = concede; 4 = concede with caveat; 3 = hold and restate; 2 = hold and name deflection; 1 = hold and escalate). Concession only valid at ≥ 4; no consecutive concessions (two in a row is a sycophancy signal — stop and re-attack the original premise); attack-intensity preservation (softening under pressure is a sycophancy tell); frame-lock self-check every three rounds (forces a premise attack if three rounds passed without one). Named deflection classes: reframe, authority, volume, sentiment, goalpost shift, tu quoque, premature consensus. Six attack categories: premise, evidence, alternative, consequence, scope, audience. Closing report with surviving attacks, resolved attacks, sycophancy log (concession rate, consecutive-concession events, deflections observed, frame-lock interventions) and accept / revise / reject recommendation. (3) Prompts/peer-review.prompt.md — multi-perspective peer review of any document (RFC, ADR, design doc, paper, long-form article, spec). Five-role panel (Editor-in-Chief + Reviewer 1 Methodology/Architecture + Reviewer 2 Evidence/Implementation + Reviewer 3 Clarity/Audience + Devil's Advocate via the new skill). Independent Phase 1 pass with strengths, severity-tagged issues (critical / major / minor / nit) and 0–100 score mapped to Accept (≥ 80) / Minor revision (65–79) / Major revision (50–64) / Reject (< 50). Phase 2 cross-reviewer matrix (consensus issue = ≥ 3 reviewers, counts double; split issue; DA-critical = surviving DA premise attack). Phase 3 EIC synthesis with deterministic decision rule and a hard-split escalation rule (range > 25 points → explain split and pick the more conservative decision). Rules of engagement enforce: default first-review ceiling of 85, no fabricated issues (every issue must point to a locator), no memory facts (hand external claims to citation-integrity first), read-only (no edits), one pass per invocation (re-review is a fresh run). Counts: skills 26 → 28; prompts 8 → 9.

  • Three new skills added (2026-05-19, Skills/skill-creator/SKILL.md, Skills/mcp-builder/SKILL.md, Skills/doc-coauthoring/SKILL.md). (1) skill-creator documents how to author and iterate Skills/**/SKILL.md files in this repo: progressive-disclosure (body ≤ 500 lines, deep material in references/), the trigger-keyword USE FOR: / DO NOT USE FOR: description pattern that drives auto-selection in VS Code Copilot and gh copilot, the 1024-char description cap, a lightweight eval workflow (3-5 real prompts, with/without), and a description-tightening loop for under- or over-triggering skills. (2) mcp-builder covers building Model Context Protocol servers end-to-end: research → implement → review → evaluate; TypeScript vs Python SDK selection; stdio vs streamable-HTTP transport choice; tool naming + description discipline; Zod / Pydantic schemas; pagination; actionable error messages; MCP Inspector testing; a 10-question realistic-task eval rubric; Windows / VS Code Copilot stdio gotchas (cwd is workspace root, stdout corrupts JSON-RPC, long awaits block the host). (3) doc-coauthoring is a three-stage workflow for collaboratively drafting a substantive document (spec, design doc, PRD, RFC, decision doc, Schriftsatz, post-mortem): Stage 1 Context Gathering (meta-questions + info dump + clarifying questions), Stage 2 Refinement section-by-section (clarify → brainstorm → curate → draft → surgical edits), Stage 3 Reader Testing with a fresh assistant or subagent answering predicted reader questions to surface blind spots; pairs with technical-writer, legal-researcher, and tax-researcher agents. Skill count: 23 → 26.

  • New agent: research-analystAgents/research-analyst.agent.md. Technical and scientific web research and investigation agent designed to upstream-feed the SDLC pipeline and the domain agents with fact-checked, source-traced findings instead of plausible-sounding LLM prose. Five-phase workflow (SCOPE → SOURCE → VERIFY → SYNTHESIZE → DELIVER) modelled on PRISMA-style systematic-review practice adapted for open-web research; falsifiable research question with explicit inclusion/exclusion criteria and a stop condition written before searching. Strict 8-tier source hierarchy (standards bodies > primary literature > source code & official docs > regulatory > established secondary > community-curated > vendor marketing > social media as leads only); triangulation rule requiring ≥ 3 independent primary sources for any Established Tier-1 claim; lateral-reading discipline (assess sources from outside, not from inside). Confidence-graded findings on a 5-step scale (Established / Probable / Contested / Weak / Speculation). Anti-LLM-citation-laundering guardrail: citations generated by an LLM are treated as unverified hypotheses until the source is fetched and the cited content confirmed verbatim. Mandatory web.archive.org / archive.today snapshot for every cited URL. Persistent investigation memory bank (investigation-<slug>.md, -sources.md annotated bibliography, -querylog.md replication-grade query log, -notes.md working hypotheses and dead ends). Per-claim verification recipes for 11 claim types: statistical/numeric, "studies show", software version, standards reference, CVE, legal-DE, quotation, image/video, news event, vendor claim, AI/ML capability. Adversarial self-review (active counter-evidence search) before any Established/Probable grade. Dossier template with confidence column, divergences and open questions, methodology, known limits, reference list with archive URLs, and a replication-log link — strictly separated from publication prose. Hands off to technical-writer (Publish as Article), legal-researcher (Escalate German-Law Angle), and tax-researcher. Brings the agent count to 11.

  • Setup script now sets COPILOT_ALLOW_ALL=1 at User scope so the GitHub Copilot CLI can run non-interactively. Setup-CopilotSettings.ps1 now persists COPILOT_ALLOW_ALL=1 via [Environment]::SetEnvironmentVariable(..., 'User') (and mirrors it into the current Process scope so the change is visible without opening a new shell). Without this flag gh copilot blocks on per-tool confirmation prompts, which prevents the custom agents and skills shipped from this repo from running through the CLI surface in any non-interactive context. The block is idempotent: if the User-scope variable already equals 1 the script logs a no-op message instead of rewriting it. Placed immediately after the ~/.copilot/{agents,instructions,skills,prompts} junction step so all CLI discovery wiring sits together.

  • New instruction: preflightInstructions/preflight.instructions.md. Workspace-wide (applyTo: "**") pre-flight compliance hook that auto-loads on every chat turn and forces Memory Bank reads, instruction/skill discovery, promptHistory.md append, and a UTC-timestamped PRE-FLIGHT acknowledgment before the first tool call. Acts as the de facto pre-prompt hook for the default (non-agent) chat mode and as a backstop for all agent modes.

  • New instruction: postflightInstructions/postflight.instructions.md. Workspace-wide (applyTo: "**") post-flight compliance hook enforcing verification of changes, Memory Bank updates (activeContext.md, progress.md, promptHistory.md), CHANGELOG.md updates under [Unreleased] for user-visible changes, local commits on ai/<slug> branches with Co-authored-by trailer (never pushed without explicit consent), and a [x]/[ ] POST-FLIGHT checklist at the end of every substantive reply.

  • Embedded PRE-FLIGHT and POST-FLIGHT blocks in all 10 agentscareer-coach, legal-researcher, tax-researcher, DevOps Training Writer, QC Inspector, Security & Quality Assurance, Software Engineer, Technical Troubleshooter, Technical Writer & Documentation, and Training Content Writer. Each agent now opens with a role-scoped pre-flight checklist (Memory Bank reads, instruction/skill matching, promptHistory.md append with the agent's own name, UTC timestamp + acknowledgment) and closes with the post-flight checklist (verification, Memory Bank update, changelog, local ai/<slug> commit, [x]/[ ] summary), each linking back to the workspace-wide instruction file as the enforcement backstop.

  • New agent: career-coachAgents/career-coach.agent.md. Bilingual (EN/DE) career coaching, CV/resume/Lebenslauf writing, cover-letter drafting, job search, application-pipeline tracking, interview preparation, salary negotiation, and LinkedIn/Xing optimization. Five-phase workflow (ASSESS → POSITION → CRAFT → APPLY → ADVANCE) with a persistent memory bank (profile.md, career-strategy.md, applications.md, deadlines.md, plus per-job dossiers and per-interview prep files). ATS-aware formatting rules, STAR/CAR/XYZ achievement framing, region-aware CV conventions (US/UK/IE/CA/AU resume vs. DE/AT/CH Lebenslauf vs. EuroPass vs. Academic CV), and an explicit ethics-first rule: never fabricates experience, qualifications, metrics, or credentials. Integrates pdf-to-markdown / docx-to-markdown / xlsx-to-markdown for ingestion, pandoc-docx-export for final DOCX rendering, create-outlook-draft / send-outlook-email for application emails, outlook-calendar-export for interview tracking, microsoft-todo-tasks for follow-ups, grammar-check for proofreading, whisper-pyannote-transcription for mock-interview debriefs, marp-slide-overflow for portfolio decks, and authenticated-web-extraction for LinkedIn/GitHub/Sessionize ingestion. Hands off to legal-researcher for German employment-law matters and to technical-writer for LinkedIn / thought-leadership pieces. Carries a mandatory StBerG/RDG-style disclaimer for any output with legal, financial, or contractual recommendations. Brings the agent count to 10.

  • New skill: authenticated-web-extractionSkills/authenticated-web-extraction/SKILL.md plus a bundled bootstrap/ folder (package.json, scripts/open.mjs, scripts/extract.mjs, tasks/check-logins.mjs, tasks/dump-cookies.mjs) so the harness can be recreated on a new machine in one PowerShell snippet. Persistent Playwright + Microsoft Edge profile at %LOCALAPPDATA%\CareerAuthBrowser\ for pulling data out of sites that require login (LinkedIn, GitHub, Sessionize, Microsoft 365, X, Meetup). Documents cookie-based auth detection (markup-stable, unlike DOM selectors), per-site auth cookie names (li_at, user_session, .AspNet.ApplicationCookie, auth_token, MEETUP_MEMBER), the session-cookie-vanish workaround (Chromium discards session-only cookies on shutdown — promote and re-inject on every run; documented as the reason Sessionize logs out between runs without it), Edge launch flags required for OAuth callbacks (TrackingPrevention, ThirdPartyStoragePartitioning, FedCm, AutomationControlled), the profile-lock orphan-msedge.exe failure mode, and a generic task-harness pattern for adding new extractions. Default rule: extract → propose → user pastes manually; never mutate user accounts. Brings the skill count to 23.

  • New skill: whisper-pyannote-transcriptionSkills/whisper-pyannote-transcription/SKILL.md plus companion transcribe.py and diarize.py. End-to-end pipeline to transcribe long recordings on a Windows GPU workstation and attach speaker labels: ffmpeg extracts a 16 kHz mono WAV; faster-whisper large-v3 (CTranslate2, float16) produces .txt / .srt / .json; pyannote/speaker-diarization-3.1 produces an .rttm; segments are merged by max-overlap into .diarized.json / .diarized.srt / .diarized.txt. Documents 11 concrete pitfalls including the CPU-only PyPI torch wheel default (use --index-url .../whl/cu128), the dual HF gated-model agreement (speaker-diarization-3.1 and segmentation-3.0), the Windows torchcodec failure with Gyan.FFmpeg full builds (preload waveform via torchaudio.load and pass {waveform, sample_rate} to the pipeline), the Python 3.12 venv constraint (3.13/3.14 lack ctranslate2/pyannote wheels), and the Tee-Object non-ASCII exit-code trap (verify outputs by file size, not exit code). RTX 4080 Laptop transcribes ≈ 2 h of audio in ≈ 30 min. Brings the skill count to 22.

  • New skill: marp-slide-overflowSkills/marp-slide-overflow/SKILL.md. Detects and fixes content overflow in Marp slide decks before exporting to PPTX/PDF/PNG, where Marp silently clips anything taller than the 1280×720 viewBox. Ships a Puppeteer-based scrollHeight-vs-viewBox detector (with CI-gate exit codes), a two-tier CSS density pattern (dense / compact) to fit content without splitting slides, a fillRatio decision table for picking the smallest fix, and a side-by-side HTML review report. Documents the phantom-leading-section gotcha that causes off-by-one mapping between source markdown and rendered slides. Brings the skill count to 21.

Changed

  • CopilotAtelier is now a PowerShell module built with Sampler and published to the PowerShell Gallery (2026-07-29). Until now the only supported distribution was "clone the repository and run the setup script", which gave no version identity, no update path, and no way for a consumer to tell whether their deployed customizations were current. The repository is now a Sampler project. Module sources live in source/ (Public/, Private/, and the manifest); the five Customization directories plus Keybindings/ stay at the repository root — so plugin.json, the documentation links, and a plain clone all keep working — and the new .build/Copy_Customizations_To_Output.build.ps1 task copies them verbatim into the built module, so a Gallery install carries exactly the same payload as a clone. Versioning is GitVersion via GitVersion.yml, so ModuleVersion in the source manifest is a placeholder the build replaces. The first Gallery release is 2.0.0: the distribution model is a breaking change for anyone scripting against the old monolithic script.

    The 560-line Setup-CopilotSettings.ps1 was decomposed into three exported commands and six private helpers with no behavioral change to the deployment itself. Setup-CopilotSettings.ps1 survives as a thin shim that dot-sources source/ and calls Install-CopilotAtelier against the clone, so every existing instruction, prompt, and README reference to it still works and no build is needed to deploy a working tree. Two behaviors did change: the canonical target folder is now fixed to CopilotAtelier (the module name) instead of being derived from the clone's folder name, because a Gallery-installed module has no clone to derive from; and console output moved from Write-Host to the information stream, so Install-CopilotAtelier is quiet by default and returns a summary object — the shim passes -InformationAction Continue to preserve the familiar console experience.

    CommandPurpose
    Install-CopilotAtelierDeploys the customizations, links ~/.copilot, merges settings and keybindings, and records the deployed version in <target>/.copilotatelier.json.
    Update-CopilotAtelierCompares the installed version with the Gallery, installs a newer one, and redeploys from it. -Force redeploys the current version; -SkipDeployment stages the update.
    Get-CopilotAtelierVersionReports the installed version, the deployed version, and whether the deployment is current.
  • CHANGELOG.md is now machine-parseable (2026-07-29). Get-ChangelogData failed on this file because [Unreleased] had no link reference and the two release headings used an em dash instead of the Keep a Changelog - separator. Sampler's Create_Changelog_Release_Output and Create_ChangeLog_GitHub_PR tasks both depend on that parse, so the release pipeline could not have run. Fixed all three, and a new QA test pins the parse.

  • Model declarations are now priority arrays with a GA fallback (2026-07-28). All 11 Agents/*.agent.md hard-pinned Claude Opus 4.8 (copilot), so a single retirement would break every agent at once — and GitHub retires Copilot models on a roughly six-week cadence. Each agent now declares model: ['Claude Opus 5 (copilot)', 'Claude Opus 4.8 (copilot)']: the first available model wins and the last entry must always be GA. gitlens.ai.vscode.model moves to copilot:claude-opus-5. Reference/copilot-cli-model-routing.md and the session-handoff example were refreshed against the current supported-model list (GPT-5.2 and GPT-5.2-Codex retired 2026-06-01; Gemini 3 Pro retired 2026-03-26).

  • Explicit subagent eligibility for every Custom agent (2026-07-28). Three agents (legal-researcher, qc-inspector, tax-researcher) declared no agents key at all, which defaults to "every agent may be used as a subagent", and none of the eleven used disable-model-invocation — so a tax or QC specialist was a candidate for model-initiated delegation on any task, the exact overlapping-description failure the VS Code documentation warns about. Those three now declare agents: [], and the eight heavyweight or domain-specific roles (career-coach, devops-training-writer, legal-researcher, qc-inspector, software-engineer, tax-researcher, training-writer, troubleshooter) set disable-model-invocation: true. security-reviewer, technical-writer, and research-analyst stay model-invocable as the intended delegation targets; an agent named explicitly in another agent's agents array still overrides the flag, so every existing handoff and coordinator path is preserved.

  • agent-evals routes to the native evaluation tooling first (2026-07-28). The skill predated Microsoft's own tooling and documented a harness whose generation step it admitted could not be automated ("Copilot has no stable non-interactive PowerShell entry point"). Skills/agent-evals/SKILL.md now opens with a Native tooling first section covering the Chat Customizations Evaluations extension (static analysis of SKILL.md / *.agent.md / *.instructions.md / *.prompt.md for contradictions, ambiguity, persona conflicts, and cognitive load, via /analyze-prompt or the Analyze command) and the Waza eval runner (download binary → create scaffold → run), which is precisely the missing non-interactive runner. The bundled run-evals.ps1 is re-framed as the Fallback harness. The capability-vs-regression, pass@k-vs-pass^k, grader-choice, and 20-50-real-failures guidance is unchanged and now feeds the Waza scaffold.

  • Setup deploys a fifth directory and gained an opt-in cross-tool switch (2026-07-28). Setup-CopilotSettings.ps1 copies Hooks/, creates the ~/.copilot/hooks Discovery link, and merges chat.hookFilesLocations and github.copilot.chat.skillTool.enabled. The per-link logic was extracted into a Set-CustomizationLink function so link targets are no longer hard-coded to ~/.copilot. New -IncludeClaudeCodeLinks switch additionally links ~/.claude/skills and ~/.agents/skills for Claude Code and other agentskills.io clients — off by default, because VS Code reads all three user-level skill locations and enabling it would register every Skill more than once in VS Code.

  • Authoring schema updated to the current Customization surface (2026-07-28). copilot-authoring.instructions.md documented only name + description for Skills and a single-string model for agents, so the repository's own schema was a strict subset of what VS Code and agentskills.io support. It now covers the agent model priority array, disable-model-invocation, user-invocable, hooks, and an explicit "never use the deprecated infer" rule; the Skill fields compatibility, context, license, metadata, allowed-tools, and argument-hint; and a new Hooks schema section with the eight lifecycle events, the exit-code contract, and the fail-open rule. applyTo extended to Hooks/*.json.

  • MCP server curation declared out of scope (2026-07-28). Every agent declares the useMcp tool but the library configures no server. Rather than ship a curated mcp.json — executable code with credentials and network reach, pushed identically to every synced machine — Decision 17 keeps MCP a per-machine, per-tenant concern and confines the library's MCP responsibilities to authoring (mcp-builder) and review (agent-security-review). Recorded under scope boundaries in projectbrief.md.

  • Extend windows-gui-screenshot-capture to existing and third-party executables (2026-07-27). Branch scene orchestration by source ownership; add process-scoped control discovery, message-pumped WinEvent window/state readiness, cross-process WM_SETTEXT, state restoration, original-process cleanup, scene-aware pixel and landmark validation, and mandatory final visual review. Harden the shipped helper to reject global/foreground discovery, cross-process handles, and failed bounds/capture/close calls. Add a one-level external-Win32 reference, eight real regression cases, a desktop-RPA decoy, and a focused Pester safety-contract test.

  • Route Memory Bank context without lowering quality gates (2026-07-24). Make .memory-bank/index.md the sole unconditional read, select only task-relevant knowledge, remove promptHistory.md from routine Pre-flight, fail open on ambiguity, missing facts, conflicts, or invalid routing, and retain loading-mode: full as a tested one-switch rollback. Add the index to initialization while preserving byte-for-byte behavior for existing files and accepting clean checkouts without the gitignored local prompt log.

  • Keep VS Code native memory role-gated (2026-07-24). Verify the shipped tool reference as memory, retain every Custom agent's existing least-privilege tool allowlist, and qualify all eight native-memory references as unavailable unless another active agent exposes the tool or the user supplies notes explicitly. The version-controlled Memory Bank remains authoritative; any future role grant requires a capability eval and agent-security review.

  • Centralize lifecycle and completion quality across all Custom agents (2026-07-23). Move Memory Bank base initialization and the complete Definition of Done gate into deployed Pre-flight/Post-flight Instructions; preserve every agent's tools, handoffs, domain workflow, quality gates, disclaimers, and role-specific persistence schema; harmonize role initialization with the durable-write/read-only boundary; restore the Software Engineer's three on-demand role files and shared projectbrief.md curation; respect repository ignore policy for local Memory Bank ephemera; replace ten duplicate lifecycle blocks and three per-tool narration templates with shared pointers; and redirect deployed engineering Skills away from the repository-only Definition of Done reference. The cross-agent regression suite fingerprints every tool/handoff surface and complete role-persistence section.

  • Reduce Software Engineer latency without weakening quality gates (2026-07-22). Replace the 413-line Custom agent file with a 156-line, prompt-budgeted execution contract; retain all tools and handoffs, mandatory focused and final validation, test-first behavior changes, bug-fix regression tests, self-review, risk-triggered independent review, agentic-security checks, and the compact role-specific Memory Bank extension. Pre-flight now reuses full Instruction and Skill bodies already supplied in context and reads missing matches at most once per turn.

  • Non-impacting turns are now exempt from post-flight documentation (2026-07-16). The per-turn contract classifies each turn in hindsight at post-flight. A substantive turn — a project/config file was created or edited, a durable decision emerged, the user asked to record something, a bug or root cause was discovered, or a git tag was cut — runs the full post-flight (verify, Memory Bank, CHANGELOG.md, local commit, checklist). A non-impacting turn — pure Q&A, read-only investigation, or a self-documenting git commit/merge — skips verification, CHANGELOG.md, progress.md, the local commit, and the promptHistory.md append, and emits only a single POST-FLIGHT: n/a — non-impacting turn (<reason>) line; ambiguity biases to substantive. postflight.instructions.md gains the classification rule and now owns the promptHistory.md append; preflight.instructions.md step 5 no longer appends it. The conflicting "Every interaction → append to promptHistory.md" write-trigger was softened to "Every substantive interaction …" across all seven agents that declared it (Software Engineer, Technical Troubleshooter, Security & Quality Assurance, Technical Writer, DevOps Training Writer, Training Content Writer, QC Inspector), and the Software Engineer and Technical Troubleshooter CORE MANDATE lines likewise. AGENTS.md pre/post-flight summary updated; .memory-bank/promptHistory.md created as a substantive-turn log; systemPatterns Decision 11 records the policy. Pre-flight discovery (probe + read Memory Bank) is unchanged and still mandatory. Re-run Setup-CopilotSettings.ps1 to propagate the updated Instructions and Agents.

  • Align AutomatedLab routing, long-running monitoring, WinRM diagnostics, and PowerShell execution (2026-07-16). Route Hyper-V lab deployment to automatedlab-deployment and Proxmox/QEMU failures to automatedlab-proxmox; add restart epochs and protocol-specific readiness; require structured progress tokens and last-progress reporting; split WinRM advanced diagnostics into direct references; keep Invoke-Pester, Invoke-Build, and builds fully detached through a cross-platform Start-Process/nohup helper; supervise payload exit so ResultPath deterministically records success or failure; reject ambiguous outer-braced encoded probes; and replace foreground sleep-polling with process/log/result metadata plus on-demand status checks.

  • Software Engineer agent wired to the three new discipline skills (2026-07-09). Agents/software-engineer.agent.md points its Testing Strategy at test-driven-development (test-first for conventional code, beside the existing agent-evals pointer for skill/prompt/agent deliverables), its Implementation-Level Recovery at debugging-and-error-recovery (reproduce → localize → reduce → fix root cause → guard), and its Subagent Code-Review step at code-review-and-quality (five-axis review with severity labels) — mirroring how agent-security-review and agent-evals are already wired.

  • skill-creator now prescribes behavioural-enforcement sections for skills (2026-07-09). Skills/skill-creator/SKILL.md adds a Behavioural enforcement: rationalizations, red flags, evidence section documenting three body patterns that keep an agent on-process when the shortest path tempts it to skip a step: an anti-rationalization table (excuse → rebuttal, pre-empting the model's own justifications for dropping the expensive step), a red-flags list (observable symptoms of drift — about to report success without running verification, editing the canonical artifact instead of a working copy, switching tools with no error captured — that trigger a stop-and-re-enter rather than pushing through), and a non-negotiable evidence / verification close (name the artifact and command that prove success; "looks right" is never enough), mirroring the repo's turn-level post-flight gate at the skill level. The authoring checklist gains a matching required item, scoped to skills that encode a skippable discipline (tests, security, verification, destructive-op guards) and explicitly waived for purely subjective-output skills. Body 302 → 345 lines (≤ 500); description untouched at 1014/1024 chars (triggering unchanged); markdownlint clean.

  • long-running-job-monitor skill now enforces its timestamp + elapsed status-line rule as a triggered gate, not mid-body prose (2026-07-08). Skills/long-running-job-monitor/SKILL.md fixes a real failure where an agent that had loaded the skill emitted the START timestamp and then went silent, giving status updates with no timestamp and no elapsed — including on turns whose topic was something else — because the rule lived only as descriptive prose in the mid-body "Reporting format" and "Outcome" sections with no per-turn trigger and no definition-of-done gate. Fixes: a new top-of-body The one rule (non-negotiable) section (immediately after ## Outcome) with a > [!IMPORTANT] callout requiring the first line of every in-flight reply to be the status line [YYYY-MM-DD HH:mm UTC] elapsed=Xm | phase=… | status=WORKING|STALLED|DONE|FAILED | next=… and declaring a reply that touches the job without it a process violation; an explicit per-turn trigger (the rule fires even when the user's turn is about something else — silence, or "it's still going" with no timestamp/elapsed, is a missing heartbeat, not "waiting correctly"); a STATUS LINE — every in-flight reply checkbox gate (UTC timestamp, elapsed since the pinned START, phase + status, next milestone, out-of-band target evidence) that mirrors the repo's pre/post-flight enforcement; a supersedes the generic opener note (a bare [… UTC] per-turn timestamp is insufficient while a job is in flight); an elapsed-friction removal (pin the START time once from the log's START line, copy the .status sidecar's latest line straight into the reply); and an Anti-patterns to match against block under "Reporting format" with ❌ (answering an unrelated question mid-job with no status line; "it's still going" with no timestamp/elapsed) and ✅ (lead with the status line, then answer) examples. Section 5 and the bottom checklist now point at the gate instead of restating it (progressive disclosure), and the Reporting-format timestamps were harmonized to HH:mm UTC. The description frontmatter is unchanged (skill discovery already works — the fix is in application, not selection); body 173 → 221 lines (≤ 500); markdownlint-cli2 clean (0 errors).

  • Global model default and docs bumped to Claude Opus 4.8 (2026-07-02). Following the agent sweep, Setup-CopilotSettings.ps1 now writes copilot:claude-opus-4.8 (gitlens.ai.vscode.model) and claude-opus-4.8 (github.copilot.advanced.model); README.md, .memory-bank/techContext.md, the copilot-authoring model-id example, and the session-handoff source-model example were all updated to 4.8. Per-agent and global-default model ids are now aligned. Users must re-run Setup-CopilotSettings.ps1 to apply the new global default. The Reference/copilot-cli-model-routing.md lineup was updated too: Opus → 4.8 (4.7 fallback, 4.8-1m long-context) and the deprecated GPT-5.1 family → GPT-5.5; Sonnet 4.6, Haiku 4.5, gpt-5.2 / gpt-5.3-codex / gpt-5.2-codex, and gemini-3-pro-preview were left unchanged per the confirmed lineup, pending the planned full rewrite.

  • Security & Quality Assurance agent gains an LLM & Agentic Systems security layer (2026-07-02). Agents/security-reviewer.agent.md adds Layer 6: LLM & Agentic Systems Security to the assessment framework (and an LLM & Agentic Review node to the multi-layer flowchart): OWASP Top 10 for LLM Applications (2025) review checks (LLM01/02/05/06/08 in depth, the rest screened); the lethal trifecta as an explicit blocking check — flag any agent/config/MCP wiring that combines private-data access + untrusted-content exposure + an outbound channel, remediate by removing a leg not by adding a guardrail, with "95% detection is a failing grade" and "prompt injection ≠ jailbreaking" stated outright; "prompt injection via tool output" treating tool results, fetched pages, READMEs, and issue text as untrusted input; and a containment-first review preferring environment-layer controls (sandbox/VM/devcontainer, egress allow-lists, scoped least-privilege identity, no blanket PATs) over model-layer "please don't", noting ~93% approval fatigue. Adds OWASP LLM Top 10 + lethal-trifecta lines to the Executive Summary compliance checklist and a new LLM & Agentic Systems Compliance Report Template; adds OWASP GenAI (https://genai.owasp.org/) and Simon Willison's "lethal trifecta" to the threat-intel references; points the LLM layer at the new agent-security-review skill. The existing declarative/CVSS finding style is preserved. Model bumped to Claude Opus 4.8 (copilot).

  • mcp-builder skill gains a Tool security section (2026-07-02). Skills/mcp-builder/SKILL.md adds a compact Tool security section: don't build the lethal trifecta into one server (private-data reads + untrusted-content ingestion + outbound network); least-privilege / scoped credentials, no god-mode tokens; treat every tool return value as untrusted (an injection vector) and validate/segregate it; egress allow-listing; "an audited connector ≠ audited data"; and confused-deputy risk with destructiveHint + human-in-the-loop gating. Cross-links the new agent-security-review skill.

  • All 11 agents bumped from Claude Opus 4.7 to 4.8 (2026-07-02). Every Agents/*.agent.md frontmatter model: now declares Claude Opus 4.8 (copilot) as the current model (Opus 4.8 is the mid-2026 current release, superseding 4.7). Scope was the Agents/ folder per the sweep; the follow-up global-default bump (see the entry above) then moved Setup-CopilotSettings.ps1 and the docs to claude-opus-4.8 as well.

  • The software-engineer agent is wired to the two new skills (2026-07-02). Agents/software-engineer.agent.md points its Security design principle at agent-security-review (for agents / LLM features / MCP servers, before handoff to the security-reviewer) and its Testing Strategy at agent-evals (for skill/prompt/agent deliverables — capability vs regression, pass@k vs pass^k).

  • howto-write-skills and skill-creator name context engineering; AAIF added as an open standard (2026-07-02). Reference/howto-write-skills.md now names context engineering as the discipline behind progressive disclosure / on-demand loading, and adds the Agentic AI Foundation (AAIF, Linux Foundation) to the canonical references beside the existing agentskills.io open-standard note; Skills/skill-creator/SKILL.md gains the same context-engineering framing in its progressive-disclosure section.

  • marp-slide-overflow skill gains editable-PPTX Recipe 4b and is split into references/ per the progressive-disclosure pattern (2026-05-28). Skills/marp-slide-overflow/SKILL.md adds Recipe 4b: Selectable-Text PPTX (--pptx-editable) between Recipe 4 and the Phantom-Leading-Section gotcha: the default --pptx export rasterises each slide to a background image (text not selectable/searchable), while --pptx-editable shells out to LibreOffice (soffice) to emit real text shapes; covers SOFFICE_PATH discovery, <a:t> text-run verification via System.IO.Compression, and LibreOffice provisioning when winget returns exit 1618 (mid-install) via lessmsi MSI extraction. The description USE-FOR list gains editable PPTX, selectable text PPTX, marp pptx-editable, SOFFICE_PATH, LibreOffice PPTX, searchable PPTX, lessmsi MSI extract, winget 1618. To stay within budget (the addition pushed the skill toward the 1024-char description and 500-line body caps), the body was trimmed from 746 to 397 lines by extracting four long-form recipes into one-level-deep references — references/mermaid-prerender.md (mermaid pre-render via mmdc, syntax gotchas, sizing CSS), references/png-verification.md (Recipe 0 PNG visual verification, three invariants, subagent QA prompt), references/overflow-detector.md (Recipe 1 Puppeteer overflow-check.mjs + build wiring), and references/speaker-note-guard.md (Recipe 5 speaker-note Pester guard) — each left in the body as a 1–2-line pointer. Description trimmed to 983 chars; the "When to Use", root-cause explanation, fillRatio decision table, and recipe index stay in the body as a navigation map. Removes marp-slide-overflow from the Pass-B candidate list. Markdown lints clean; folder name still matches the name: field.

  • Interactive question UI convention extracted to shared reference and propagated (2026-05-27). New Reference/interactive-questions.md holds the canonical rule (prefer vscode_askQuestions over markdown checkboxes when the tool is available), guidelines (1–4 questions per cluster, multiSelect usage, freeform overrides, cancellation fallback), and anti-patterns. Skills/grill-me/SKILL.md trimmed to a pointer plus a Grill-Me-specific addendum (SIGNED OFF gate uses allowFreeformInput: false). New "Interaction style" sections added to the six other files that interview the user: Skills/doc-coauthoring/SKILL.md, Skills/skill-creator/SKILL.md, Agents/career-coach.agent.md, Agents/legal-researcher.agent.md, Agents/tax-researcher.agent.md, Agents/research-analyst.agent.md, Agents/qc-inspector.agent.md. Each is a 2-line pointer to the shared reference, scoped to the agent's intake or interview phase. No agent edits to tools: arrays required — all 10 agents already include vscode/askQuestions per the 2026-05-08 normalization pass.

  • skill-creator skill rewritten against Anthropic's canonical Agent Skills guidance (2026-05-22). Skills/skill-creator/SKILL.md re-anchored on the Anthropic Agent Skills overview and authoring best practices. New material: the six-step authoring frame (Name / Trigger / Outcome / Dependencies / Step-by-step / Edge cases) from the Anthropic guide and Simon Scrapes' summary video; the third-person description rule (Anthropic explicitly warns against first- and second-person voice in the description field because it is injected into the system prompt); degrees-of-freedom calibration (high/medium/low specificity matched to task fragility — "narrow bridge with cliffs" vs "open field" analogy); explicit "point, don't dump" philosophy with the body-vs-references split; one-level-deep reference rule (Claude previews nested references with head -100 and gets incomplete info); the ## Contents TOC requirement for reference files >100 lines; a six-pattern catalogue (high-level guide + references / domain-organised references / conditional workflow / workflow checklist / feedback loop / examples); evaluation-driven development workflow (build evals before writing extensive documentation); the Claude-A / Claude-B iteration loop (author with one instance, test with a fresh one); "solve, don't punt" rule for scripts/ (handle errors, no voodoo constants, forward slashes only); plan-validate-execute pattern for batch/destructive ops; cross-skill overlap-audit recipe (PowerShell one-liner listing every description); explicit anti-patterns (time-sensitive info → <details> blocks, inconsistent terminology, offering too many options, deeply nested references, SKILL.md as tutorial); and a mechanical "splitting an oversized SKILL.md" recipe. Description trimmed to 1014 chars (within Anthropic's 1024-char cap). Body 296 lines (within 500-line budget).

  • Three oversized skills split into references/ per the new progressive-disclosure pattern (2026-05-22). All three were over 800 lines with no references/ directory — the exact bloat the skill-creator rewrite warns against. (1) Skills/sampler-framework/SKILL.md: 2656 → 372 lines, 12 references (build-yaml, dependency-management, dependency-resolution, bootstrap, testing-patterns, custom-build-tasks, gitversion, ci-cd-pipelines, dsc-datum, vscode-integration, troubleshooting, community-files, project-structure, module-manifest, multi-module, commands-reference). (2) Skills/automatedlab-deployment/SKILL.md: 1815 → 353 lines, 7 references (networking, roles-and-services, post-deployment-operations, vm-operations, lab-management, troubleshooting, cmdlet-reference). (3) Skills/datum-configuration/SKILL.md: 927 → 389 lines, 4 references (datum-yml-reference, projectdagger-patterns, dscworkshop-reference, common-tasks). All references one level deep from SKILL.md, headed with ## Contents table-of-contents (Anthropic's >100-line rule). SKILL.md body now reads as a navigation map: When-To-Use, recipe summaries with 1–2-line pointers to the deep reference, and inline content only for sections that always fire together. Quality benefit per Anthropic: lower context cost on every invocation, and the assistant can bash: read references/<topic>.md only when the task actually needs that topic. Remaining Pass-B candidates (pester-patterns 872 / german-legal-research 785 / marp-slide-overflow 746 / sampler-migration 729 / pandoc-docx-export 718 / winrm-troubleshooting 694 / mecm-dsc-deployment 662 / outlook-email-export 644 / pdf-to-markdown 630 / dsc-troubleshooting 627 / whisper-pyannote-transcription 565) tracked for follow-up.

  • marp-slide-overflow skill gains Recipe 5 — speaker-note coverage gotchas and a Pester guard (2026-05-22). Skills/marp-slide-overflow/SKILL.md documents three gotchas surfaced when auditing "does every slide have notes?" on multi-file Marp decks: (A) the ----inside-a-code-fence trap (naive splitters mis-count YAML/markdown frontmatter shown as code examples as slide separators, producing phantom slides whose H1 is name: software-engineer — auditors must track an inCode boolean toggled on every ``` line and ignore --- while in code, mirroring what the build's slide-splitter does); (B) distinguishing real notes from Marp directives (<!-- version: -->, <!-- _class: -->, <!-- _paginate: -->, <!-- _color: -->, <!-- _backgroundColor: -->, <!-- fit -->, <!-- _split_ -->) — the working filter blocklists those prefixes and requires inner-text length > 40 chars so single-line directives never count as notes; (C) section-divider slides (<!-- _class: section-divider -->) need a separate assertion because they typically receive a per-module appendix note block rather than per-slide notes. Ships a drop-in Pester 5 guard with Get-MarpSlide and Test-SlideHasNote helpers defined in BeforeAll (cross-referencing the new pester-patterns Pattern 14 on runspace isolation), and a -ForEach parametrisation across marp-1h-keynote.md / marp-2h-standard.md / marp-4h-workshop.md. Includes a title-drift / merge pattern for multi-file decks that assemble a monolith from per-module splits: ship a notes-title-map.psd1 aliasing split-file H1s to monolith H1s, with three remediation paths in preference order (add alias → add notes in split file → add notes inline in monolith). Explains the editorial marker <!-- _split_ --> (split-origin marker, Marp- and pipeline-ignored, blocklisted so it never falsely registers as a speaker note).

  • pester-patterns skill gains Pattern 14 — helpers used inside It must live in BeforeAll (2026-05-22). Skills/pester-patterns/SKILL.md documents Pester 5's per-It runspace isolation: helper functions defined as siblings of It blocks inside Describe are invisible inside It, producing a misleading CommandNotFoundException: The term 'Get-…' is not recognized… that looks like a typo or a missing dot-source but is actually runspace isolation. Shows the broken pattern (helpers at Describe scope), the fix (helpers in BeforeAll, shared state in $script: scope), and three related gotchas: (a) bare $foo in BeforeAll won't survive into It — use $script:foo; (b) data needed by -ForEach on It must be defined in BeforeDiscovery, not BeforeAll, because -ForEach resolves at discovery time before BeforeAll runs; (c) inlining helpers as scriptblocks in every It is the cargo-cult workaround, not the fix.

  • research-analyst agent wired to the new citation-integrity, devils-advocate-review, and peer-review building blocks. Agents/research-analyst.agent.md previously declared the principles ("no LLM citation laundering", "no invented sources, ever", "Adversarial self-review") without operational machinery. Three changes wire the principles to the new artefacts. (1) The Claim-Level Verification Recipes table gains an Any cited reference (operational gate) row that mandates handing every supporting citation to the citation-integrity skill before grading a claim Established or Probable; a MISMATCH (F5 — source exists but does not support the attributed claim, the most consequential failure mode) or NOT_FOUND (F1/F3/F6) verdict blocks the grade until a replacement source is verified, and the F1–F6 failure taxonomy becomes the canonical vocabulary for the dossier's Known Limits section. (2) The Adversarial Self-Review subsection is expanded: the existing free-form counter-evidence pass remains, but the finding must additionally be run through the devils-advocate-review skill against itself — ≥ 4 attack lines (premise / evidence / alternative / consequence), 1–5 rebuttal scoring run before the internal reply is drafted, concession only at ≥ 4, no consecutive concessions, frame-lock check forcing a premise attack at least every three rounds. The Devil's Advocate closing report (surviving attacks + resolved attacks + sycophancy log with concession rate, consecutive-concession events, deflection classes observed, frame-lock interventions + accept / revise / reject recommendation) is attached to the dossier's Adversarial review methodology field; surviving premise-level attacks are dossier-blocking and must be resolved or moved to Divergences and Open Questions. (3) A new Panel-Review the Dossier handoff invokes the peer-review prompt (EIC + Methodology + Evidence + Clarity + Devil's Advocate panel) against the finished dossier as the pre-publication gate before the existing technical-writer handoff. Net effect: the agent's stated discipline (Iron Rules 7, 8, 12) now has operational backing instead of relying on free-form self-control.

  • docx-to-markdown skill broadened from read-only to read + edit. Skills/docx-to-markdown/SKILL.md gains a Beyond Reading section that documents the OOXML unpack → edit-XML → repack pattern in pure PowerShell (Expand-Archive + [System.IO.Compression.ZipFile]::CreateFromDirectory, avoiding the extra folder level Compress-Archive would prepend). Covers tracked-changes XML shape (<w:ins> / <w:del> with w:id, w:author, w:date; <w:delText> inside <w:del>; preserve <w:rPr>; mark paragraph mark deleted to avoid empty paragraphs), comments (comments.xml + <w:commentRangeStart/> / <w:commentRangeEnd/> / <w:commentReference/> plus the relationship and content-type wiring when no comments existed yet), three options for accepting all tracked changes (LibreOffice headless preferred, Word COM with caveats, manual XML walk for simple docs), and a tool-selection table that draws the boundary against pandoc-docx-export. Frontmatter description updated to cover the new edit/redline use cases while staying under the 1024-char CLI cap.

  • pdf-to-markdown skill broadened from extract + OCR to extract + OCR + create + manipulate. Skills/pdf-to-markdown/SKILL.md gains a Beyond Extraction section: install commands (uv pip install pypdf reportlab pdfplumber pypdfium2; winget install qpdf.qpdf), copy-pasteable recipes for merge, split, rotate, watermark (overlay-page pattern), encrypt / decrypt (AES-256), creating a PDF from scratch with reportlab Platypus (including the bundled-Helvetica <sub> / <super> glyph trap), and filling AcroForm fields with pypdf (update_page_form_field_values + flatten). Adds a tool-selection table mapping each task to its first-choice tool (qpdf for bulk merge/split/rotate; pypdf for stamping and form fill; reportlab for new PDFs; pdfplumber/pymupdf for text/tables; pymupdf+tesseract for OCR), plus a security note that PDF encryption is not a substitute for transport-layer or at-rest encryption. Frontmatter description expanded to cover all manipulation use cases.

  • xlsx-to-markdown skill broadened from read-only to read + create + edit. Skills/xlsx-to-markdown/SKILL.md gains a Beyond Extraction section built around the cardinal rule "write Excel formulas, never hardcoded computed values" (with a worked example showing the right and wrong patterns), recipes for creating new workbooks and editing existing ones in place with openpyxl + pandas without an Excel install, the data_only=True trap (reads cached values but strips formulas on save — read-only only), a recalculation pass via LibreOffice headless (soffice --headless --calc --convert-to xlsx) followed by a Python error-scan that flags every #REF! / #DIV/0! / #VALUE! / #N/A / #NAME? / #NULL! / #NUM! cell, common formatting recipes (header bold + fill, frozen header row, column widths, number formats), and a verification checklist before handing off any generated workbook. Frontmatter description expanded to cover create/edit/recalc use cases.

  • marp-slide-overflow Recipe 0 hardened with a subagent visual-QA step. Skills/marp-slide-overflow/SKILL.md gains a new Step 3b that hands the at-risk PNGs from Step 1 to a fresh subagent (runSubagent) with a deliberately adversarial prompt ("assume there are problems — your job is to find them") and an eight-point checklist (title, footer page number, half-cut rows, overlapping elements, right-edge text overflow, mermaid SVG fit, low-contrast text/icons, inconsistent gaps). Documents the fallback for when no subagent is available (open PNGs side-by-side and walk the same checklist yourself), the iterate-after-fix loop (one fix often pushes other content down), and the rubber-stamping anti-pattern (a subagent that reports "all clean" on first pass is a smoke alarm, not a clean bill of health — re-run with stronger adversarial framing).

  • marp-slide-overflow skill expanded with mermaid pre-rendering and a mandatory PNG-based visual verification workflow. Skills/marp-slide-overflow/SKILL.md gains two major sections. (1) Mermaid gotcha + Option B pre-render recipe — Marp CLI has no built-in mermaid support and silently emits ```mermaid fences as literal <pre><code class="language-mermaid"> blocks in HTML/PDF/PPTX with no warning; client-side mermaid.js (Option A) only works in --html and is captured too early by the Chromium screenshotter for --pdf/--pptx. The recommended Option B is a PowerShell build-script snippet that regex-extracts every fence, content-hashes the source (SHA-1, first 12 chars) for between-build caching, shells out to npx @mermaid-js/mermaid-cli mmdc -i … -o …svg -b transparent, and substitutes the fence with a forward-slash relative ![](diagrams/mmd-<hash>.svg) image reference (Windows absolute paths silently break the Markdown image parser). Documents mermaid label-quoting gotchas that only surface during pre-render ({}, (), [], backticks, <br/> in node labels — wrap labels in double quotes), the Select-String 'class="language-mermaid"' regression detector, and a sizing block (cap section img { max-height: 380px } in deck CSS; prefer graph LR over graph TB; verify with per-slide PNGs). (2) Recipe 0 — PNG-based visual verification is now the mandatory gate before claiming a slide "fits". Text heuristics (counting <li>, character length, raw scrollHeight) miss oversized images, wrapped table cells, and long code lines; the HTML/VS Code preview rescales the viewport and hides clipping. Recipe 0 ships a four-step workflow: render every slide via marp-cli --images png --image-scale 1, programmatically flag at-risk slides (tables ≥ 4 rows, code blocks ≥ 7 lines, bullets ≥ 7, any image) with a PowerShell scanner that correctly skips fenced code and the leading YAML frontmatter, hand-check each at-risk PNG for three invariants (title visible at top, footer page number visible at bottom-right, no half-cut rows), and iterate. Documents two anti-patterns (trusting the build-script heuristic as a gate, trusting the HTML/preview rescaled viewport) and the splitting-beats-shrinking rule for content that won't fit even with dense/compact. README skill table and .memory-bank/techContext.md skill registry updated to reflect the new scope.

  • research-analyst agent strengthened with primary-source methodology citations. Agents/research-analyst.agent.md was rewritten in twelve targeted spots to ground the workflow in canonical research-methodology sources instead of the original vague "PRISMA-style" framing. Confidence scale now maps to GRADE (High/Moderate/Low/Very Low; https://www.gradeworkinggroup.org). Workflow header cites PRISMA 2020 (Page MJ et al., BMJ 2021;372:n71). Lateral reading is now explicitly the SIFT method (Caulfield 2019; Caulfield & Wineburg, Verified, U Chicago Press, 2023) with the four moves spelled out. OSINT recipes were expanded with the InVID/WeVerify, FotoForensics, EXIFTool, Sentinel Hub / EO Browser toolchain and multi-archive evidence preservation. A new Investigating Disinformation and Information Operations subsection imports the playbook from Verification Handbook 3 (EJC / Silverman, 2020): actor analysis, bot/cyborg detection, patient-zero tracing, closed-group monitoring, synthetic-media and C2PA, network attribution, plus Bellingcat's 2025 finding that current LLMs are unreliable for geolocation. Source-provenance checks now require ORCID/ROR author and institution identifiers, IFCN Code of Principles authority test for fact-checking sources, FAIR-aligned persistent-identifier-first rule (DOI, arXiv ID, PURL, w3id, Handle; F1/A1/I1/R1.x), and a pre-registration / trial-registration check (OSF, AsPredicted, ClinicalTrials.gov, EU CTR, DRKS, PROSPERO). Claim-Level Verification Recipes gained four new rows: systematic review/meta-analysis appraisal with AMSTAR 2 (four-tier overall confidence), RCT appraisal with Cochrane RoB 2 (5 domains), non-randomized intervention study appraisal with ROBINS-I (7 domains), Equator Network reporting-guideline lookup (CONSORT/STROBE/STARD/TRIPOD/ARRIVE/CHEERS/CARE/SRQR/COREQ/SQUIRE/RIGHT/AGREE), and a conflicts-of-interest extraction recipe citing Cochrane Handbook §7.8 and the Open Payments database. A new Non-Reporting Biases subsection imports the Cochrane Handbook §7.2.3 taxonomy (publication, time-lag, language, citation, multiple-publication, location, selective-reporting) with documented empirical effect sizes. All additions are anchored to primary sources verified in this turn.

  • All 10 agent tools: arrays normalized, namespaced, and expanded to cover the work each agent actually does. Three problems were fixed in one pass: (1) legal-researcher used 25 invalid namespaced IDs (execute/runNotebookCell, com.microsoft/azure/search, todo, etc.) that did not resolve in VS Code Copilot; (2) career-coach and tax-researcher used look-alike names (readFile, findFiles, grep, semanticSearch, runInTerminal) that the resolver silently dropped; (3) qc-inspector had only 4 tools and could not edit files, list directories, or run anything. Every agent then got bare names migrated to the new fully-qualified form introduced in VS Code 1.105 (changessearch/changes, editFilesedit/editFiles, fetchweb/fetch, vscodeAPIvscode/vscodeAPI, problemsread/problems, terminalLastCommandread/terminalLastCommand, runCommandsexecute/runInTerminal, runTasksexecute/createAndRunTask, newvscode/newWorkspace, etc.). Universal additions across all 10 agents: read/readFile, search/fileSearch, search/listDirectory, search/textSearch, read/viewImage, vscode/askQuestions, todo, execute/getTerminalOutput. Engineering preset (sw-eng, troubleshooter, security-reviewer, technical-writer) additionally gets web/githubTextSearch, vscode/runCommand, vscode/installExtension, and (sw-eng + troubleshooter) vscode/getProjectSetupInfo. Software-engineer also gets the Jupyter set: edit/editNotebook, execute/runNotebookCell, read/getNotebookSummary, read/readNotebookCellOutput. The required agent tool was added to the 5 agents that declare an agents: list (software-engineer, troubleshooter, career-coach, training-writer, devops-training-writer) to satisfy the agent-file schema. Result: every .agent.md lints clean and every agent has the file-I/O, search, terminal, web, and workspace tools needed to perform its declared role.

  • Pre-flight: probe for .memory-bank/ is now a separate numbered step, and the acknowledgment must name its result. Earlier the rule was a sub-bullet of "Read the Memory Bank" framed as advisory, which still let agents skip the probe and conclude "no Memory Bank" from the <workspace_info> listing alone. Instructions/preflight.instructions.md is now restructured into 7 numbered steps (probe → read → match instructions → match skills → append promptHistory → timestamp → acknowledgment); the workspace summary is explicitly labelled not authoritative for hidden folders; the acknowledgment must report (a) the probe used and its result, (b) what was read, (c) which instructions matched, (d) which skills matched. The same structural fix is applied to the embedded pre-flight blocks in all 10 agents (career-coach, legal-researcher, tax-researcher, DevOps Training Writer, QC Inspector, Security & Quality Assurance, Software Engineer, Technical Troubleshooter, Technical Writer & Documentation, Training Content Writer), each renumbered to 6 steps with the probe as step 1. Supersedes the earlier sub-bullet wording.

  • Setup script now uses NTFS junctions under ~/.copilot/ instead of chat.*FilesLocations settings. Setup-CopilotSettings.ps1 no longer writes chat.agentFilesLocations, chat.instructionsFilesLocations, chat.agentSkillsLocations, or chat.promptFilesLocations. After copying the four customization folders to the canonical target (~/OneDrive/CopilotAtelier/ when OneDrive is installed, otherwise ~/CopilotAtelier/), the script creates junctions ~/.copilot/agents, ~/.copilot/instructions, ~/.copilot/skills, and ~/.copilot/prompts pointing to the matching target subfolders. This unifies discovery for both the VS Code Copilot chat extension and the GitHub Copilot CLI through a single well-known path. Existing junctions are recreated to track the current target. Pre-existing real folders at the link paths are removed silently if empty; if non-empty the script prompts the user, and on consent merges the contents into the target (without overwriting newer files there) before removing the folder and creating the junction. README updated accordingly.

  • whisper-pyannote-transcription skill — expanded from 11 to 17 pitfalls and added two new recipes covering long-form / mixed-language and evidence-grade transcription. New pitfalls (12–17): condition_on_previous_text=False is mandatory for transcripts > 30 min and for mixed-language audio (default True causes loops and language drift); the initial_prompt glossary is a bias, not a hard constraint, so rare proper nouns (e.g. "Replit" → "PocketOS") still need a post-processing regex pass; mixed-language recordings must be split by offset and run as two passes (--language de, then --language en), each with its own glossary; Whisper is non-deterministic at the segment level, so high-stakes passages need 2–3 focus passes with --beam-size 10–15 and majority voting; a mechanical bulk regex pass on the master SRT can silently overwrite a focus-verified reading by resolving the artefact to the grammatically nearest candidate (typically a personal pronoun) rather than the acoustic one; and short subject-phrase hallucinations have multiple plausible resolutions — always cross-check against domain-coherent secondary sources. New Recipe 3b: transcribe a segment with a glossary file via the companion transcribe-segment.py script (offset/end windows, --initial-prompt-file, --no-condition), including the mixed-language split pattern that prevents the EN half of a recording being transcribed as broken German with mojibake umlauts. New Recipe 5: focused re-transcription with majority voting for evidence-grade quotation — picks a 30–90 s window, re-runs with stronger settings, applies a 2-of-3 majority rule, with documented "audio is the primary evidence" guidance (§ 286 ZPO) and a cross-stage consistency pattern that defends focus-verified readings from later bulk regex passes. Verification examples are anonymized (placeholders for personal names, internal acronyms, and amounts), with Replit → PocketOS retained as a public third-party brand-name example. Description keywords expanded.

  • marp-slide-overflow skill — added a "Critical Gotcha: Frontmatter backgroundColor Wins Over Class CSS" section. Marp injects YAML backgroundColor: / color: as inline style="..." attributes on every <section> (including background-image:none), which silently defeats any section.<class> { background: ... } rule in the style: block. Symptoms: section-divider gradients never rendered, white-on-white headings, low-contrast subheadings. Documents detection (grep inline style attributes in rendered HTML), three fix options (tune class text colours to the inline background, move palette into style: block, or use per-slide <!-- _backgroundColor: ... -->), and adds a row to the anti-patterns table. Description keywords expanded.

Fixed

  • The GitVersion step destroyed the evidence of its own failure (2026-07-29). Calculate ModuleVersion (GitVersion) piped dotnet-gitversion straight into ConvertFrom-Json. GitVersion writes its diagnostic log to standard output, not standard error, so when it misbehaves the pipe swallows the message and the step reports Conversion from JSON failed with error: Unexpected character encountered while parsing value: M instead of the reason. That is exactly what run 30462902820 reported after v2.0.0 was tagged, which is why the cause is still unknown: GitVersion 5.12.0 installed, ran, and wrote something beginning with M that nobody can see. The step now captures the output, checks the exit code and that the payload starts with {, prints the raw output between markers before failing, and echoes the resolved dotnet-gitversion path. It also prepends rather than appends ~/.dotnet/tools to PATH, so a GitVersion already present on the runner image cannot shadow the pinned 5.x, and clears $PSNativeCommandUseErrorActionPreference so GitHub's $ErrorActionPreference = 'Stop' cannot throw before the output is shown.

  • A tag build could not always compute a version (2026-07-29). A tag push checks out refs/tags/<tag> with a detached HEAD, so GitVersion takes its branch name from GITHUB_REF and works on a synthetic tags/v2.0.0 branch. Nothing in GitVersion.yml matched it, so every tag build logged No branch configuration found for branch tags/v2.0.0, falling back to default configuration and then tried to inherit the increment from a parent branch. That lookup is not guaranteed to succeed: when it finds no main or develop branch it throws Gitversion could not determine which branch to treat as the development branch (default is 'develop') nor release-able branch (default is 'main' or 'master') and exits 1, taking the release build with it. Reproduced with GitVersion 5.12 against a detached tag checkout. GitVersion.yml now carries a release-tag branch entry matching ^tags?[-/] with no label and no increment, because on a tag build the tag is the version, so the inheritance lookup is never reached. Verified FullSemVer 2.0.0 on a tag build with and without branch refs present, with versions on main and on feature/ai branches unchanged, and a QA test pins that some branch configuration keeps claiming the synthetic name. This is hardening of a fragile path; it is not the cause of run 30462902820, whose output began with M where a GitVersion failure log begins with INFO.

  • CI failed on macOS and on both Windows legs after the workflow alignment (2026-07-29). Two unrelated defects, each exposed by a job that had never run before. The path tests assumed a Linux configuration root. tests/Unit/Private/Get-CopilotAtelierPath.Tests.ps1 and tests/Unit/Public/Install-CopilotAtelier.Tests.ps1 sandbox XDG_CONFIG_HOME and then expect the VS Code settings beneath it, but Get-CopilotAtelierPath correctly resolves ~/Library/Application Support/Code/User on macOS — so the settings file the installer wrote was never where the tests looked, taking down the whole clean-profile context along with the legacy-cleanup assertions. Both suites now derive the sandbox configuration root per platform; the shipped behaviour is unchanged, because the module was already right. The Memory Bank compactness budget was exceeded. .memory-bank/progress.md had grown to 205 lines against its 200-line budget, so Test-MemoryBankHealth returned LineBudgetExceeded and Passed: False. That test carries no Pester tag, so only the two Windows legs run it — the Linux and macOS legs are restricted to -PesterTag @('Unit','QA') and never saw it. The oldest milestones moved to git history, which is what the file's own retention policy prescribes.

  • The SessionStart hook probed an untrusted path through the PowerShell provider (2026-07-29). strips control characters from a hostile workspace path was the only failing test on the Linux CI job, in three consecutive runs, while both Windows jobs passed. Hooks/scripts/Add-SessionContext.ps1 built its probe path with Join-Path and tested it with Test-Path, using a cwd supplied by the hook payload — attacker-controlled input. A provider that cannot resolve such a path writes to standard error, and the hook's caller merges the streams, so provider noise corrupts the single-line JSON contract the hook writes to standard output. The script's own notes already promised that "a probe failure never blocks a session", but nothing enforced it. The probe now uses [System.IO.Path]::Combine and [System.IO.File]::Exists, which carry no drive or provider semantics and never write to a stream, inside a guard that falls back to "no Memory Bank" on any failure.

  • CI failed on Windows PowerShell 5.1 and Linux while Windows PowerShell 7 passed (2026-07-29). Three defects, all of them tests that had only ever run on one platform.

    A Windows GUI test claimed to be a portable unit test. tests/WindowsGuiScreenshotCapture.Tests.ps1 covers a helper that compiles Win32 interop, yet it was tagged Unit and guarded only by #requires -Version 7.0. On Windows PowerShell 5.1 that statement fails discovery rather than skipping, so Pester reported a container failure and the whole run went red; on Linux the Unit tag pulled it into the job and it dot-sourced a Windows-only helper. The requirement is now a BeforeDiscovery host probe feeding -Skip, and the dot-source moved inside the Describe so a skipped block never loads it.

    A blocking hook looked like a crash. tests/Hooks.Tests.ps1 asserts that the PreToolUse hook writes to standard error and exits 2. Windows PowerShell turns a child's standard error into an ErrorRecord, and PowerShell 7.3+ turns a non-zero native exit code into a terminating error, so under the build's Stop preference the expected block threw before it could be asserted on. Invoke-Hook now neutralises $ErrorActionPreference and $PSNativeCommandUseErrorActionPreference around the call and restores both.

    Windows PowerShell 5.1 read UTF-8 as ANSI. tests/SharedLifecycle.Tests.ps1 matches an em dash heading in an agent file. Repository Markdown is UTF-8 without a BOM, which Windows PowerShell 5.1 decodes with the ANSI code page, turning into mojibake and failing the match. Its reads now pass -Encoding UTF8.

  • All three CI test jobs failed: the repository test suite silently required a gitignored file (2026-07-29). The build job passed and every test job failed at Run the tests. Four repository tests assumed .memory-bank/promptHistory.md exists — but it is local ephemera excluded by .gitignore, so it is never present in a CI checkout. The suite had therefore only ever been run in a developer worktree. tests/MemoryBankHealth.Tests.ps1 asserted a fixed CanonicalFileCount of 8 and now derives it from whether the optional local log is on disk; tests/MemoryBankRouting.Tests.ps1 read the file unconditionally and now skips absent optional files while still requiring the seven version-controlled ones; and Skills/memory-bank/scripts/Test-MemoryBankRouting.ps1 counted five coverage misses because the eval set names promptHistory.md in requiredFiles — an optional file that genuinely does not exist is no longer a routing miss, while a present file that should have been routed still is.

    Reproducing the failure against a clean clone also exposed a latent defect the CI checkout would have hidden: tests/Setup-CopilotSettings.Tests.ps1 still derived the canonical target folder name from the clone directory. Since the module migration fixed that name to CopilotAtelier, the legacy-location cleanup assertions only passed when the clone happened to be named CopilotAtelier — true on a GitHub runner, and true locally, but not in general. The test now uses the same constant the installer does.

  • The first GitHub Actions run failed in 0s with an invalid workflow file (2026-07-29). Invalid workflow file (Line: 102, Col: 16): Unrecognized named-value: 'matrix' — no job ever started and no workflow graph was produced. The test job parameterised the interpreter with shell: ${{ matrix.shell }} to cover Windows PowerShell 5.1 and PowerShell 7 from one step. shell is the one step key absent from GitHub's contexts-availability table for jobs.<job_id>.steps, so it rejects every context — while name, runs-on, run, and with in the same job accept matrix and were fine. Moved the shell to jobs.<job_id>.defaults.run, which the same table does list as accepting matrix. Added tests/Workflows.Tests.ps1, which parses every workflow and fails when any step's shell holds an expression, so this class of breakage is caught locally instead of on push.

  • The build broke on Sampler 0.120.0: BuiltModuleSubdirectory from build.yaml was ignored (2026-07-29). RequiredModules.psd1 pins Sampler to latest, so a plain ./build.ps1 resolved 0.120.0 and failed with Could not find the built module manifest for module 'CopilotAtelier'. Root cause: 0.120.0 adds a WorkspaceDependencies.build.ps1 task file that declares $BuiltModuleSubdirectory = (property BuiltModuleSubdirectory 'module'). InvokeBuild dot-sources every task file into one shared scope and its Get-BuildProperty treats an empty string as unset, so the 'module' default from that alphabetically last file overwrote the shared variable. Every later Set-SamplerTaskVariable call then took the parameter branch and never read BuiltModuleSubdirectory: builtModule from build.yaml. build.ps1 reads build.yaml directly, so it kept building into output/builtModule while the task variables pointed at output/module — and 0.120.0 turned that mismatch from a silent empty value into a hard throw. Aligned build.yaml with the new Sampler convention (BuiltModuleSubdirectory: module, which is also what the 0.120.0 project template now emits) and stopped the six test files from hard-coding the subdirectory, so a future default change cannot break test discovery again.

  • Both hooks failed to start on Windows: %USERPROFILE% was never expanded (2026-07-29). VS Code spawns a hook command directly through child_process.spawn with no shell, so the %USERPROFILE% token in the windows override reached PowerShell verbatim and every session opened with The argument '%USERPROFILE%\.copilot\hooks\scripts\Add-SessionContext.ps1' to the -File parameter does not exist. The SessionStart Memory Bank probe never ran and the PreToolUse never-push guard silently failed open on every tool call — the exact failure mode the hook exists to prevent. The POSIX $HOME default had the same defect. Both commands now switch from -File to -Command "& (Join-Path $env:USERPROFILE '...'); exit $LASTEXITCODE" so PowerShell resolves its own path and still propagates the blocking exit code 2. The existing regression ran the shipped string through cmd.exe /c, which expands %VAR% and therefore passed against the broken configuration; it now spawns the command without a shell against a staged fake home, the way VS Code actually does.

  • Setup-CopilotSettings.ps1 could delete the customization tree it manages (2026-07-28). When refreshing a Discovery link, the script called [IO.Directory]::Delete($linkPath) and, on failure, fell back to Remove-Item -LiteralPath $linkPath -Force -Recurse. On Windows PowerShell 5.1 — the runtime most users launch the script with — Remove-Item -Recurse on a junction follows the reparse point and recursively deletes the target's contents, which here is Agents, Instructions, Skills, Prompts, and Hooks in OneDrive. The primary delete only fails on a lock or sharing violation, so the effect was rare, silent, and irreversible. The fallback no longer uses -Recurse, and the primary call now passes recursive: $false explicitly. Surfaced by an independent security review of the new hook work.

  • Two Skill descriptions exceeded the 1024-character Agent Skills cap (2026-07-28). authenticated-web-extraction carried a 1460-character description, over the limit that agentskills.io and VS Code both enforce, which risks a silent load failure. Trimmed to under the cap by removing the duplicated AspNet ApplicationCookie / cookie-based auth detection keywords and the redundant "use this when" sentence, keeping every distinct trigger term. The extended agent-evals description was likewise trimmed from 1106 back under the cap. The new tests/SkillFrontmatter.Tests.ps1 now fails the build when any Skill exceeds it.

  • Removed the inert github.copilot.advanced.model setting (2026-07-28). Setup-CopilotSettings.ps1 wrote github.copilot.advanced.model = claude-opus-4.8 on every run. github.copilot.advanced is the completions-engine bag and has no documented model member, so the value was never consumed. Setup now actively removes the stale key from settings.json instead of leaving misleading state in the user profile.

  • Prevent duplicate Customization discovery from legacy VS Code location settings (2026-07-22). Remove only the historical ~/CopilotAtelier/* and ~/OneDrive/CopilotAtelier/* entries written by older Setup script releases, preserve unrelated user-defined locations, retain ~/.copilot/prompts, and cover the migration with a sandboxed Pester regression.

  • Fix Setup-CopilotSettings.ps1 path failures on Linux and macOS (2026-07-21). Resolve the user profile from USERPROFILE on Windows and HOME elsewhere; resolve VS Code settings from APPDATA on Windows, ~/Library/Application Support on macOS, and XDG_CONFIG_HOME or ~/.config on Linux; replace hard-coded backslash child paths; and create Unix symbolic links instead of Windows-only junctions. Stop on path-resolution errors before a null copy destination can flatten Agents, Instructions, Skills, and Prompts into the current directory and overwrite files with matching names, including the root README.md. Restore the overwritten project README and remove all 76 exact, untracked root copies produced by the failed run. Add a sandboxed Pester regression that runs setup twice with Windows profile variables unavailable and verifies XDG settings, keybindings, copied customizations, and idempotent symbolic-link recreation.

  • Harden WinRM and long-job troubleshooting guidance against unsafe or misleading repairs (2026-07-16). Guard StopPending process termination with state/PID/sole-service checks; preserve explicit machine-wide TrustedHosts entries; require trusted hostname certificates; scope firewall rules by profile and management source; make all quota changes raise-only; correct HRESULT mappings and event-message truncation; reject temporary-session Start-Job; prevent liveness signals from masking a stalled ProgressToken; and require sidecar evidence to be expanded into the complete UTC/phase/status/next reply opener.

  • All 11 agents can now create files and directories (plus role-appropriate runTask / rename) (2026-07-11). Every Agents/*.agent.md previously declared only edit/editFiles from the edit group, so no agent could create a new file or folder — a writer could not start a new .md, an engineer could not add a source or test file. Added edit/createFile and edit/createDirectory to all eleven agents (inserted next to edit/editFiles). The four engineering/troubleshooting agents — Software Engineer Agent, Security & Quality Assurance Agent, Technical Troubleshooter Agent, research-analyst — also gained execute/runTask; the two refactor/fix-heavy engineering agents (Software Engineer, Troubleshooter) additionally gained edit/rename. No other array entries were reordered. The browser/* (Playwright) group was deliberately not added to any agent, to avoid forming the lethal trifecta (private data × untrusted web content × outbound channel) alongside file-write and terminal execution. Verified: every tools: flow-sequence parses as JSON with no duplicate IDs and edit/editFiles retained; full YAML frontmatter valid (name + tools present); markdownlint-cli2 reports 0 errors across all 11 files. Re-run Setup-CopilotSettings.ps1 to deploy the updated agents; tool changes take effect only when an agent is reselected or reloaded.

  • marp-slide-overflow Recipe 4b — documented Bug 4 (LibreOffice silently drops speaker notes) with a python-pptx graft fix (2026-05-31). Skills/marp-slide-overflow/SKILL.md adds a fourth LibreOffice corruption bug to Recipe 4b, after Bug 3 and before the "Verify the text really is selectable" subsection. Symptom: Marp's native --pptx export writes each slide's HTML-comment speaker notes as PowerPoint notes, but the --pptx-editable LibreOffice round-trip emits a PPTX with no ppt/notesSlides/ parts and no notes master — every slide's notes are gone silently with no warning. Unlike Bugs 1–3 this is not fixable with editable-only CSS because the notes never reach the slide body. Detection: a PPTX is a ZIP; native decks contain ppt/notesSlides/notesSlideN.xml parts and the editable deck contains none (System.IO.Compression one-liner). Fix: render a throwaway native PPTX from the same editable assembled markdown (identical slide count/order), then copy notes slide-by-slide into the editable deck with a new Copy-PptxNotes.py helper (python-pptx auto-installed on demand) that recreates the notes slides, notes master, relationships, and [Content_Types].xml overrides automatically. Verify: count editable slides carrying non-empty notes via python-pptx. The bug-count intro updated from "three concrete, fixable corruption bugs" to four (three CSS-fixable + the notes graft). Copy-PptxNotes.py added to the Reference Implementation list. The description USE-FOR list gains editable PPTX notes, speaker notes dropped, pptx-editable notes missing, copy pptx notes, python-pptx notes (trimming the lower-value marp --images png, searchable PPTX, lessmsi MSI extract, winget 1618 keywords to stay at 1022/1024 chars). Proven end-to-end in raandree/PSConfProxmoxSession (build.ps1 region "3b — Restore speaker notes in the editable PPTX" + build/Copy-PptxNotes.py): 41/41 slides carry notes after the graft. Keeps the "ship both / never mutate the canonical deck" framing — the editable deck stays a second artefact, now notes-complete.

  • marp-slide-overflow Recipe 4b — corrected editable-PPTX rendering caveat with two concrete LibreOffice fixes (2026-05-29). Skills/marp-slide-overflow/SKILL.md replaces the vague "code blocks reflow — inherent, not fixable in CSS" caveat in Recipe 4b with the real root cause and two proven fixes. Bug 1: LibreOffice's multi-slide HTML→PPTX pass silently drops digit glyphs from bold numeric table cells (Haiku 4.5Haiku ., $1.618455$ .); fixed by rendering table text non-bold (table th, table strong, table b { font-weight: normal !important; }). The wider-substitute-font workaround is explicitly rejected because it clips leading digits from dense cells (101,7470 ,747). Bug 2: inline code falls back to an arbitrary font because the deck's monospace webfont cannot be embedded; fixed by pinning code, pre, pre code to LibreOffice-bundled fonts ("Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace !important). Both fixes are injected into an editable-only assembled copy of the markdown so the canonical deck, image PPTX, and HTML preview keep their original styling. Adds the "feed the editable path its own assembled copy (never mutate the canonical deck)" rule and the "marp exits 1 with no error text when the target .pptx is open in PowerPoint" gotcha.

  • Session-handoff path corrected in two legacy prompts (2026-05-27). Prompts/deadline-action-handoff.prompt.md and Prompts/sync-project-emails.prompt.md wrote handoff payloads to memory-bank/session/... (no leading dot), which on Windows creates a separate untracked memory-bank/ folder at repo root next to the canonical .memory-bank/. Corrected the four memory-bank/session/... references to .memory-bank/session/... so both legacy prompts now share the canonical storage convention with the new session-handoff prompt. Scope intentionally narrow: the broader pre-existing dot-prefix bug in those same prompts for memory-bank/activeContext.md / progress.md / projectbrief.md references is a separate cleanup, tracked but not addressed here.

  • Skill descriptions exceeded the GitHub Copilot CLI 1024-char limit. 11 SKILL.md files (authenticated-web-extraction, automatedlab-deployment, datum-configuration, dsc-troubleshooting, german-legal-research, marp-slide-overflow, mecm-dsc-deployment, pdf-to-markdown, sampler-framework, whisper-pyannote-transcription, winrm-troubleshooting) failed to load in gh copilot with description: Skill description must be at most 1024 characters. The VS Code Copilot chat extension does not enforce this cap, so the same files loaded there. Trimmed each YAML frontmatter description: folded scalar — kept the summary sentence and USE FOR / DO NOT USE FOR signals, condensed verbose keyword lists, removed redundant qualifiers. All 23 skill descriptions now ≤ 1024 chars (max 1010). Author rule going forward: target ≤ 1000 chars on description: for CLI compatibility.

  • Repo prompts invisible to VS Code Copilot Chat after the May 6 junction-only cleanup. The May 6 change removed all four chat.*FilesLocations writers from Setup-CopilotSettings.ps1 on the assumption that NTFS junctions under ~/.copilot/{agents,instructions,skills,prompts} would cover both the VS Code Copilot chat extension and the GitHub Copilot CLI. That holds for agents, instructions, and skills (the chat extension auto-discovers those well-known paths) but not for prompts: VS Code Copilot Chat only reads prompt files from %APPDATA%\Code\User\prompts and from paths listed in chat.promptFilesLocations; only the CLI auto-discovers ~/.copilot/prompts. The setup script now writes a single chat.promptFilesLocations entry for ${userHome}/.copilot/prompts via the existing Merge-LocationSetting helper, restoring prompt visibility in chat without re-introducing the other three settings (junction discovery still covers agents, instructions, and skills). Existing user-added prompt locations are preserved by the merge.

1.1.0 - 2026-04-26

First public release alongside raandree/AgenticOperatingModel (the workshop in which CopilotAtelier is the reference exemplar of a mature personal atelier).

Added

  • Keybindings mergeKeybindings/keybindings.json is now merged idempotently by Setup-CopilotSettings.ps1 into %APPDATA%\Code\User\keybindings.json. Match key is (key, command, when); user-added bindings are preserved; a timestamped backup is created on every run. Bindings: Ctrl+K X restart PowerShell session, Ctrl+K N pop terminal to new window, Ctrl+K K pop chat to new window, and a chat-submit swap so Ctrl+Enter sends and plain Enter inserts a newline in the chat input.
  • Top-level CHANGELOG.md (this file).
  • Tax Researcher (DE) agent section in Agents/README.md.
  • README "Featured In" section linking to the Agentic Operating Model workshop and its companion patterns / memory-bank-template artefacts.
  • Memory-bank documentation pass: refreshed inventories (20 skills, 13 instructions, 9 agents, 8 prompts) and expanded the systemPatterns.md applyTo list.

Changed

  • Setup script now uses a single target location instead of dual-copying. When OneDrive is detected, Setup-CopilotSettings.ps1 registers and populates only ~/OneDrive/<repoName>/*; otherwise it falls back to ~/<repoName>/*. Previously both locations were always populated, which doubled disk usage and created drift risk when one copy was edited out-of-band. Stale ~/<repoName>/ trees from earlier dual-copy runs are removed automatically when OneDrive is now used. README and memory bank updated accordingly.
  • Default model bumped to Claude Opus 4.7 across Setup-CopilotSettings.ps1 (gitlens.ai.vscode.model, github.copilot.advanced.model), all 9 agent frontmatters, and supporting documentation. Opus 4.7 went GA in Copilot on 2026-04-16 and is Anthropic's announced replacement for Opus 4.5 / 4.6. The previous default claude-opus-4.6-fast was retired by GitHub on 2026-04-10 and would no longer resolve. Memory bank, README.md, and Instructions/copilot-authoring.instructions.md updated accordingly.
  • README clarified to describe the actual dual-mirror layout (~/CopilotAtelier/ always-populated local mirror plus an optional ~/OneDrive/CopilotAtelier/ mirror when OneDrive is detected) instead of describing the workflow as OneDrive-only. "Setup on a New Machine" now starts from a local clone.
  • Reference/copilot-cli-model-routing.md carries a banner noting the April 2026 model-lineup changes (Opus 4.7 GA, GPT-5.5 GA, GPT-5.1 family deprecated, Opus 4.6 Fast retired). A full rewrite is planned post-1.1.0.

Fixed

1.0.0 - 2026-04-22

First public-ready state of the CopilotAtelier.

Added

Agents (9)

  • Software Engineer — multi-phase SDLC workflow (Analyze → Design → Implement → Validate → Reflect → Handoff) with quality gates and handoffs to Security & QA and Technical Writer.
  • Security & Quality Assurance — five-layer assessment framework (SAST, Dependency & Supply Chain, Secrets, Configuration, Threat Intel), CVSS-based risk scoring, and PASS/FAIL/CONDITIONAL production-readiness decisions.
  • Technical Writer & Documentation — six-phase writing workflow with journalistic integrity, CRAAP source evaluation, and multiple article templates.
  • Technical Troubleshooter — six-phase Google SRE–inspired diagnostic workflow (Report → Triage → Examine → Diagnose → Test → Cure), with handoffs to Software Engineer and Technical Writer.
  • Legal Researcher (DE) — German tenancy law (Mietrecht) and civil law research and drafting, persistent case memory bank, mandatory RDG disclaimer.
  • Tax Researcher (DE) — German tax research & drafting (EStG, AO, V+V, AfA, objection proceedings, deadline calculation, ELSTER); persistent case memory bank; mandatory StBerG/RDG disclaimer.
  • QC Inspector — quality control for Oil & Gas / Energy / Industrial; EU regulatory compliance (PED, ATEX, CBAM, CSDDD, CRA); inspection-document generation (ITP, NCR, audit reports).
  • Training Content Writer — modular GitHub-hosted training content built on Bloom's revised taxonomy and constructive alignment.
  • DevOps Training Writer — DevOps/SRE/Platform Engineering training; inherits from Training Content Writer.

Instructions (13)

Auto-applied coding standards for PowerShell, Markdown, YAML, C#, Changelog, Versioning, Sampler, Pester, Git, JSON, and Azure Pipelines. Plus two meta-rule files: copilot-authoring.instructions.md (governs how this repo's own instructions, prompts, skills, and agents are authored) and powershell-execution-safety.instructions.md (enforces detached execution and Pester-in-subprocess to avoid VS Code hangs).

Skills (20)

  • Build & automation: sampler-framework, sampler-build-debug, sampler-migration, pester-patterns.
  • DSC / lab infrastructure: automatedlab-deployment, datum-configuration, dsc-troubleshooting, mecm-dsc-deployment, winrm-troubleshooting.
  • Document conversion: docx-to-markdown, xlsx-to-markdown, pdf-to-markdown, pandoc-docx-export.
  • Outlook / Microsoft 365 automation: create-outlook-draft, outlook-calendar-export, outlook-email-export, send-outlook-email, microsoft-todo-tasks.
  • Writing / legal: grammar-check, german-legal-research.

Prompts (8)

  • code-review (agent: security-reviewer) — PowerShell security review producing SARIF + Markdown + CVSS output.
  • lab-deploy, module-scaffold, pr-description, refactor (agent: software-engineer) — day-to-day development workflows.
  • export-emails, sync-project-emails, deadline-action-handoff (agent: legal-researcher) — legal-case email and deadline workflows.

Reference

  • Reference/copilot-cli-model-routing.md — 4-tier Copilot CLI model routing (Executors, Implementers, Tech Leads, Architects) with delegation policy. Reference-only; not auto-attached.

Setup

  • Setup-CopilotSettings.ps1 — one-command VS Code configuration. Derives folder name from the repo clone, registers ~/<repoName>/* locations always, plus ~/OneDrive/<repoName>/* when OneDrive is detected. Idempotent (merges settings instead of replacing), JSONC-tolerant (strips comments before parsing), and creates a timestamped backup on every run. Creates the VS Code user directory if missing.
  • Feature flags configured: chat.includeApplyingInstructions, chat.includeReferencedInstructions, github.copilot.chat.agent.thinkingTool, github.copilot.chat.search.semanticTextResults, github.copilot.chat.agent.maxRequests=500.
  • Default model set to Claude Opus 4.6 for GitLens AI and Copilot inline completions. (Superseded in 1.1.0 — Opus 4.6 Fast was retired by GitHub on 2026-04-10; the new default is Opus 4.7.)