Skip to content

feature/Review-fix-first · L-260924-736ae2 - #76

Merged
lchoquel merged 18 commits into
devfrom
feature/Review-fix-first
Sep 25, 2026
Merged

lchoquel merged 18 commits into
devfrom
feature/Review-fix-first

Conversation

@lchoquel

@lchoquel lchoquel commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Fixes the defects the 2026-09-24 plugins review ranked first. The changes are one shared MTHDS language reference, the lab stopping inputs at run-ready, run keeping saved runs out of git, the catalog saving by path, and qualified pipe codes. They also cover quoted hook commands with a Codex Bash arm, a stricter build (deep-merged tables, pruned outputs, whole-file-set orphan and marker checks), and a scaffold that plants no repository inside another. Three review rounds tightened the shared ignore procedure, the catalog's reading of a conflict with its own inline save, and the scaffold's reading of the enclosing repository, where a directory that repository ignores now gets a repository of its own, per the 2026-09-25 ruling.

Closes L-260924-736ae2
Closes L-260912-d23005
Closes L-260923-019e50
Closes L-260924-62c8f7
Leaves L-260924-9fd751
Leaves L-260924-deb436
Leaves L-260924-f00719
Leaves L-260924-afcd06
Leaves L-260924-677dd4
Leaves L-260925-5037c1
Leaves L-260925-d6d5d8

🤖 Generated with Claude Code

https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u


Summary by cubic

Fixes the defects the 2026-09-24 plugins review ranked first and carries three review rounds that tightened the save, scaffold, and build paths. Merges dev, bringing the README's open Codex install fold.

Skills

  • One shared MTHDS language reference replaces design's own and the shared one, corrected against the spec; native content types document YesNo, Date, Time, and Composite.
  • pipelex-inputs stops at run-ready without offering the run when the lab hands it a case.
  • pipelex-run checks saved runs with git check-ignore, adds an anchored /runs/ entry, and holds the save while git still sees it; prepared inputs get the same guard.
  • pipelex-catalog saves by path so every save stays linked; inline saves are offered only when the workshop refuses a path, and a conflict that is the session's own save is carried on.
  • pipelex-integrate sets PIPE_CODE to the verdict's pipe_ref verbatim, domain included, and pipelex-run passes a non-main pipe's pipe_ref.

Build and scaffold

  • Hook commands quote the plugin root so paths with spaces work; the Codex hook matches shell patches and checks absolute paths only.
  • gen_skill_docs.py deep-merges target tables so overrides keep credential wiring, prunes stale files, and check compares whole file sets; root targets no longer overlap other targets' directories.
  • commit-pristine.sh prints inside:/nested:/unversioned: verdicts and never commits in the enclosing repository; a directory that repository ignores now gets a repository of its own.
  • pdm init gets -p <dir> and --no-git, and the reference warns when a pyproject.toml above would add the project to its workspace.

Written for commit 1718b55. Summary will update on new commits.

Review in cubic

lchoquel and others added 18 commits September 24, 2026 22:14
Design, edit, explain, inputs and integrate now read one MTHDS language reference, skills/shared/writing-mthds.md, rendered per target from templates/skills/shared/writing-mthds.md.j2. Design's static references/writing-mthds.md is its base, what only the retired mthds-reference.md.j2 carried is folded in, and the PipeFunc warning is included from its partial instead of copied.

The two files had drifted, and the shared one contradicted the MTHDS standard on PipeParallel's output, on bare-string structure fields and on domain-qualified pipe references. The merged reference is corrected against the specification on those points and on sequence steps, the template category, and the optional output a "continue" condition needs. pipelex-explain now reads a PipeBatch by its own fields, and the native content types document YesNo, Date, Time and Composite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
The lab hands each test case to pipelex-inputs during setup, before it writes the case's answer key, and pipelex-inputs always ended by offering the run, so a yes ran the case before its key existed and broke the lab's first guard. A calling skill can now ask pipelex-inputs to stop at run-ready with no run offer, and the lab asks it to for every case.

The exemption names the caller, and the same bullet says that a user who said not to run is still offered the run, since an offer starts nothing, so it cannot be read as leave to drop the offer whenever a user asks to stop short. pipelex-inputs paid for the bullet by dropping three clauses that restated a step or gave a reason. Both new sentences are registered guards, and a test in each skill's suite pins the hand-off contract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
pipelex-run saves every completed run's main_stuff.json into runs/<run_id>/ under the workshop's working directory, usually a project root in a repository, and nothing ignored it, although that output carries what the run read in the user's files. In a git repository the skill now checks runs/<run_id>/ with git check-ignore before saving, adds an anchored /runs/ to the nearest .gitignore when git does not ignore it, says so, and holds the save for the user when git still does not.

The procedure is now said by pipelex-inputs, pipelex-lab and pipelex-run, so it lives in one include, templates/skills/shared/git-ignore.md.j2, parameterised by what each skill checks and the entry it writes. Inputs renders exactly as before; the lab now checks a case's paths before adding its directory, like the other two. Run paid for the paragraph by moving rationale clauses to docs/decisions.md and turning Follow a run's restatement of step 6's readings into a pointer. The guard registry, the shared-include tests and a run test pin the result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
The shared submission convention offered inline files as the fallback, but the workshop writes pipelex-method.json only beside a root file sent by path, so a method created from inline files was linked to nothing and the directory's next save created a second method. pipelex-catalog now sets a parameter of the validate-call include that makes the path form required for a save and, when the workshop refuses a path, says the save cannot be linked from this session and saves inline only on the user's yes.

The include also stops promising that any absolute path works: it names the workshop's refusal of a path outside its working directory and the relaunch that cures it. The rationale for preferring the path form moved to docs/decisions.md, which pays for the clause, so every including skill renders shorter than before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
pipelex-integrate told the model that the run's pipe_code is the bare code and its call-site templates stripped the domain, and pipelex-run said to pass "a pipe's code". The run path accepts a qualified domain.pipe_code unchanged and the runtime resolves it as an exact key, while a bare code is searched across the bundle's domains and fails as ambiguous once two of them declare it. The call sites now set PIPE_CODE to the verdict's pipe_ref verbatim, the signature fallback builds the ref from the pipe's own file's domain, refresh mode migrates a bare PIPE_CODE, and pipelex-run passes a non-main pipe's pipe_ref as pipe_code. Both skills render slightly shorter; the dropped run rationale and the gate's evidence are recorded in docs/decisions.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
…fix 6)

Every harness runs a hook command through a shell, and the Claude and Codex commands left the plugin root unquoted, so a plugin installed under a path holding a space never started its hook (the shell exits 127). Both now wrap the root in double quotes, the Vibe command and its install instructions keep the absolute path quoted, and a unit test runs every rendered command from a root whose name holds a space.

The Codex matcher also admits Bash, which is how Codex reports a patch run through the shell in a form it does not apply itself; the vendored check.mjs reads the patch from tool_input.command, and the wrapper's pre-filter now requires a patch header naming a .mthds file, so any other shell command ends before Node starts. A live Codex 0.153.4 session showed that a plain apply_patch heredoc Codex intercepts raises no PostToolUse at all, so no matcher can check it; docs/hooks.md records which forms reach the hook and that a relative path is read from the session's directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
…ted file (fix 7)

A target's table variable now merges into the defaults' key by key, so the documented dev override of [vars.mcp_server] command and args keeps env_vars and user_config instead of silently building a plugin without its credential wiring. scripts/check.py asserts that wiring on every target, reading the expectation from targets/defaults.toml rather than from the target's own variables, which the dropped wiring agreed with.

The build now owns each target's output directory: it removes whatever no template or source produces, leaving a leaked .j2 and any file git ignores, and refuses a target whose source overlaps the repository's own directories or another target's output. The freshness check compares each directory's whole file set, so a stray file under hooks/, mcp/ or skills/shared/ is reported with a cure that works, and the Vibe fragment's template has its own list so a missing one is named as an MCP template. The build-error marker is looked for in every generated text file, and the stale comment about Jinja's default Undefined is corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
…note (fix 8)

Louis ruled on 2026-09-24 that the initializer branch follows the method-app branch: a project directory inside another repository's work tree gets no git init and no pristine commit. commit-pristine.sh now prints the new verdict inside: <root> there, writes only the project's .gitignore and leaves the enclosing repository's index and HEAD alone; write-env-file.sh accepts such a directory; the skill reports the project as new files of that repository, and the GitHub reference covers both branches. The skill paid for the verdict with two reasons, now in docs/decisions.md.

The initializers reference recommended --module esnext --moduleResolution bundler for the minimal TypeScript project, which tsc compiles and plain Node runs. Reproduced on TypeScript 7.0.2 and 5.9.3 under Node 24.17, that setup type-checks clean and fails to load the generated binder with ERR_MODULE_NOT_FOUND, as pipelex-integrate says. The reference now says both ES module setups fail and offers a CommonJS emit, which loads it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
The Codex hook resolved a shell patch's relative path against the session's directory, while the patch may have run in a workdir or after a cd that the payload never reveals. It then checked a same-named file the patch never touched, blocking on it or reformatting it in place, and left the edited file unchecked. The wrapper now runs check.mjs from its own hooks directory, which ships no .mthds file, for any call but the patch tool's, so a relative path in a shell patch names no file and goes unchecked while an absolute one is still checked. The patch tool's call keeps the session's directory, where Codex applies it. This is a stopgap until the pipelex-sdk-js bundle that reads the payload's cwd is vendored. The tests, docs/hooks.md, the decision record and the changelog entry say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
…eep pdm from planting a repository

commit-pristine.sh printed inside: for a project directory the enclosing repository ignores, and the skill then called the project new files of a repository that never sees them. It now prints ignored: there, initialises and commits nothing as before, and the report says that nothing versions the project and that making it a repository is the user's choice, in words the initializers reference gives beside the env verdict's. In such a directory the enclosing rule hides the project's own .gitignore, so both scripts now judge its ignore lines by the project's own rules, which keeps write-env-file.sh from refusing the key or leaving .env out of the project's .gitignore. The initializers reference passes --no-git to pdm init, which otherwise makes a repository even inside another. The skill pays for its words with the dry-run rationale, now recorded in docs/decisions.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
…gitignore

The lab checked a case's files before copying them, so a .gitignore of *.pdf read the copies as ignored, added no entry, and left the key and inputs.json written later to the next git add. It now checks the case directory itself, which reads not ignored there and gets its entry, while a case under an already-ignored lab/ still gains none. pipelex-run wrote the literal /runs/ into whichever .gitignore was nearest, which from a repository root names a runs/ beside the root rather than the workshop's in app/, so the re-check failed and every save waited on the user. The entry is now the workshop's runs/ anchored from that file, and step 7 says the check is not for a dir the user named. Run pays for the words with two reasons now recorded in docs/decisions.md, and the unit tests pin both readings against git.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
The scaffold's initializers reference gave `pdm init --non-interactive --no-git` beside `poetry new <dir>` and `hatch new <name>`, but `pdm init` takes no directory argument and initializes whichever directory the shell stands in. The recipe now passes `-p <dir>`, which PDM 2.29.2 was seen to create, with nothing written in the starting directory and, with `--no-git`, no repository inside an enclosing one.

The same check found that under any directory holding a `pyproject.toml`, `pdm init` makes the new project a member of that project's workspace and writes into the user's file, with no flag or setting against it. The reference now says so and offers poetry, hatch or uv, or a directory outside that project, instead of running pdm there. The decision, and why the global-project flag was declined, is in docs/decisions.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
commit-pristine.sh read only `git rev-parse --show-prefix`, which is empty at any repository's root, so a project an initializer had made a repository inside the user's work tree (create-astro does, with --yes) was committed in or had its commit kept, and the GitHub reference then offered `gh repo create`. When the prefix is empty and the parent directory lies in a work tree, the script now writes the .gitignore, commits in neither repository, leaves <dir>/.git in place and prints `nested: <root>`.

The skill names the verdict beside `inside:` and `ignored:`, the initializers reference words it for the report (offer the removal of <dir>/.git, never perform it) and tells the model to pass an unlisted initializer its no-git flag inside another work tree, and the GitHub reference treats it as `inside:`. The reason is recorded next to the existing ruling in docs/decisions.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
An inline save on the update arm updates the stored method but cannot rewrite pipelex-method.json, so the link keeps its old synced_updated_at and the directory's next save is refused at expected_updated_at, which conflict.md then called somebody else's save. The same happened after any update whose link write failed, where step 5 said to fix the cause and save again.

The catalog's inline sentence now names the cost on both arms. conflict.md first checks whether the refusal's stored updated_at is the one this session's last save reported, says so when it is, and carries the save on with that updated_at as the expectation. Step 5 names a pull into the same directory as what refreshes a stale link once the workshop can write there, and the stop-table row points at the new first check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
…cedure

pipelex-inputs step 5 kept its own rule for inputs.prepared.json, which carries every non-file value of inputs.json word for word: it added the entry with no second check and no tracked-path guard. The shared include now names the prepared file among the paths it checks, before step 5's call, and step 5's sentence is gone, which also frees room in a skill near the ceiling.

pipelex-run exempts a dir the user named from its ignore check, yet its stop-table row sent a refused dir back into runs/<run_id>/ with no word of the check. The row now takes the check first, and step 7's check moved above the paragraph that saves, so it reads before the write it guards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
check_output_dir skipped every other target whose source is "./" when it gathered the directories a target may not overlap. A root target writes skills/, hooks/ and mcp/ at the repository root, and hooks/ and mcp/ are not among the repository's own directories, so a target with source = "hooks/" passed the check and its pruning deleted the root target's hook files. No shipped target is a root target, but a target TOML without a source line is one.

For each root target, the check now adds one directory per directory directly under templates/, derived rather than listed, and the refusal names the root target and the directory. A test builds a root target beside a hooks/ or mcp/ target and expects the refusal with nothing touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
… own

Louis ruled on 2026-09-25 that a project directory its enclosing repository ignores gets a repository on main and the pristine commit, as outside every work tree, since a repository there is invisible to the enclosing one while an unversioned project has no baseline and falls to its git clean -fdx. commit-pristine.sh now asks the enclosing repository from the directory's parent, by its physical path, with no trailing slash, and initializes and commits there; the ignored: verdict is gone, and a repository root under an ignoring enclosing repository is committed in rather than reported nested:.

What counts is the directory: under * then !*/, or <name>/*, the directory is seen though every file is ignored, so it gets no repository and a new unversioned: verdict. Both scripts judge .env and node_modules/ by the project's own .gitignore whenever it has no repository of its own, so a repository made there later cannot take the key, and write-env-file.sh refuses an ignored directory the pristine commit has not made a repository yet. The nested: words offer to remove only a .git the initializer planted, the uncreated copy's recipe follows the initializer, and the ruling is recorded in docs/decisions.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
Brings in the README's open Codex install fold. CHANGELOG.md conflicted in the Unreleased Changed section, where both sides added entries; the resolution keeps the branch's two entries and dev's one, in that order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoxU85gAGjneWj89vQ2Y2u
@lchoquel
lchoquel merged commit 06ea37d into dev Sep 25, 2026
8 checks passed
@lchoquel
lchoquel deleted the feature/Review-fix-first branch September 25, 2026 00:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant