Skip to content

feat: generated INDEX, .claude/workflows scaffolding, orchestration docs - #95

Merged
tigers1997 merged 2 commits into
mainfrom
feat/audit-open-items
Aug 25, 2026
Merged

tigers1997 merged 2 commits into
mainfrom
feat/audit-open-items

Conversation

@tigers1997

@tigers1997 tigers1997 commented Aug 25, 2026 •

Copy link
Copy Markdown
Owner

What & why

Rescoped after review. This PR originally carried a fourth, unrelated feature (PowerShell hook variants). The AI reviewer blocked it as scope creep under CONTRIBUTING's one-logical-change rule — correctly; I'd flagged the same risk in the original scope box. That commit now lives in its own PR stacked on this one. Nothing else changed: this is the original first commit, untouched.

Closes three of the four gaps the currency audit left open. All three are the same kind of gap — a Claude Code surface the configurator had not caught up to.

1. templates/INDEX.md is now generated. It was hand-maintained and had gone stale enough to mislead — still referencing a configurator.html that no longer exists, and missing half the modules. Produced from MODULES by python3 configure.py --write-index, with --check failing when the committed copy and the generator disagree. It cannot drift again.

2. .claude/workflows/ scaffolding. Dynamic workflows have been a first-class surface since CC 2.1.154 and the configurator shipped nothing for them. The multi-agent module now scaffolds .claude/workflows/spec-fanout.js (runs as /spec-fanout): N variants of one spec into disjoint slots, then each variant screened against the spec before reporting.

It is deliberately not a review workflow — /code-review already does multi-agent review, and shipping our own would repeat the overclaiming the previous PR just fixed. It is instead the workflow-native successor to the /infinite skill in the same module, which is exactly where that PR told users to go. --check gained a rule validating every shipped workflow's meta block and rejecting import(), both of which the runtime refuses.

3. Agent teams, channels and routines — documented, not templated. docs/04 gains a table comparing the five ways to run work in parallel by who holds the plan, and states plainly why there's nothing to scaffold: teams are spawned in conversation and live for a session; the channel gate keys channelsEnabled and allowedChannelPlugins are managed-settings only, so a project cannot enable them; routines are scheduled cloud agents running against a repo rather than your checkout, where a Stop hook is the project-scoped equivalent. Only teammateMode was worth a stub.

Type of change

  • feat — new module / skill / feature (minor bump)
  • fix — bug fix (patch bump)
  • docs — documentation only
  • chore — tooling, CI, release plumbing
  • refactor — no behavioral change
  • BREAKING

Scope

  • One logical change. One commit: catch the configurator up to Claude Code surfaces it had not covered.
  • Modules affected: multi-agent, core
  • Personas affected: small-team (gains .claude/workflows/spec-fanout.js); snapshots regenerated.

Tests

  • python3 configure.py --check passes locally — verified on this commit alone, not just at the top of the stack, so the split is genuinely clean.
  • Persona snapshots updated.

CHANGELOG

  • Added an entry under ## Unreleased.
  • I will SHA-anchor the entry after merge.

License & NOTICE

  • My contribution is my own work.
  • No AGPL-incompatible code.
  • No third-party code added.
  • templates/discipline-skills/ untouched by this PR.
  • LICENSE / NOTICE untouched.

Signing

  • All commits are signed. — Not signed (no key on the authoring machine). A squash-merge signs the result.
  • Conventional Commits prefix.

I understand

  • An automated AI review will run on this PR.
  • No merge is possible while any required check is red.
  • My contribution rights are described in CONTRIBUTING.md.

Fourth of five stacked PRs — based on fix/currency-corrections, with feat/powershell-hooks stacked on top.

@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: BLOCK

@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: BLOCK

Blocking: Scope creep -- two unrelated features in one PR (CONTRIBUTING.md requires one logical change per PR).

Feature 1 (commit 94c284b): feat: generate templates/INDEX.md and scaffold .claude/workflows/ -- adds render_template_index(), the --write-index flag, spec-fanout.js workflow, docs/04 additions, and --check rules for meta blocks and the generated index (configure.py:689-800).

Feature 2 (commit 6849cb2): feat(hooks): PowerShell hook variants behind --hook-shell powershell -- adds POWERSHELL_INLINE_COMMANDS, powershell_hook_stems(), swap_hook_shell(), apply_hook_shell_to_settings(), powershell_hook_command(), --hook-shell CLI flag, ASCII/BOM enforcement in run_check(), README and docs/03 updates (configure.py:2265-2363).

Both have their own CHANGELOG entries and separate commits. Neither depends on the other. Rolling either back would require untangling shared configure.py edits.

Recommend splitting into two PRs. Each is already well-scoped and would PASS on its own -- the blocking issue is structural, not technical.

Advisory: none. Both features are internally consistent and well-documented.

@tigers1997
tigers1997 force-pushed the feat/audit-open-items branch from 6849cb2 to 94c284b Compare August 25, 2026 15:41
@tigers1997 tigers1997 changed the title feat: generated INDEX, .claude/workflows scaffolding, orchestration docs, PowerShell hooks feat: generated INDEX, .claude/workflows scaffolding, orchestration docs Aug 25, 2026
@tigers1997

Copy link
Copy Markdown
Owner Author

Block accepted — the scope-creep call was right, and the PR body had already conceded the risk ("the PowerShell commit is self-contained and independently revertible if you'd rather take it separately"). Should have split it before asking for review rather than offering it as an option.

Done, at the commit boundary you identified:

No rewriting was needed: both commits were already atomic and each carried its own CHANGELOG entry, so the split is a branch cut, not a re-authoring. git diff between the old tip and #95 + #97 is empty.

One thing I checked rather than assumed, since a split can look clean and not be: python3 configure.py --check passes on this commit alone, not merely at the top of the stack. Commit 1 adds the generated-INDEX currency rule and commit 2 adds the .ps1 ASCII/BOM rule, so a bad cut would have left one rule enforcing against files the other commit introduces. It doesn't.

@claude review

@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: COMMENT-ONLY

Advisory

configure.py:189 - import detection in --check misses mid-file static imports

The workflow validation gate checks: if "import(" in text or text.lstrip().startswith("import "):

The first clause catches dynamic imports anywhere in the file. The second clause only catches a static ES-module import at the very first line. A static import appearing after the meta block would pass --check silently but still be rejected by the runtime. The shipped spec-fanout.js has no imports so this is not a present defect, but a future template author could be confused. Consider re.search to catch all top-level static imports.

configure.py:187 - meta field presence check uses the first closing brace as a delimiter

if field not in text.split("}", 1)[0]:

This works for the shipped spec-fanout.js (name: and description: appear before the first } in the nested phases array). It would produce a false positive if a future workflow author placed a top-level meta key after the first nested object closes. Minor; a multiline regex or balanced-brace parse would be more robust.

Both are nits; neither blocks.

@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: COMMENT-ONLY

Blocking

None.

Advisory

1. Schema-claim hygiene — workflowSizeGuideline and teammateMode stubs not cross-checked against SchemaStore

templates/core/dot-claude/settings.local.json.example (and the mirrored example) gain two new stubs:

  • // workflowSizeGuideline — noted as CC 2.1.219+
  • // teammateMode — no CC floor cited inline

Both are above the project's tested_up_to of 2.1.150. The established pattern of placing pre-verified keys in .example files is the correct holding zone, and the .example suffix means CC won't parse them directly. However, per CONTRIBUTING.md § "Schema-claim hygiene", keys added to any template should be cross-checked against SchemaStore with a citation. Neither stub references a SchemaStore entry or confirms the key is present. No blocking impact given the .example isolation, but worth adding a compat-comment citation in the next compat survey if not already planned.

2. Workflow meta validation heuristic is fragile for future workflows (configure.py:987)

The check:

if field not in text.split("}", 1)[0]:

splits on the first } in the file. For spec-fanout.js this works because name: and description: precede the first closing brace of the phases array entries. But a future workflow whose meta starts with a property containing a nested {} literal before name:/description: would produce a false meta is missing "name:" error — causing --check to fail on a valid workflow. Recommend matching on the full meta block rather than splitting on the first }.

3. Static import detection is incomplete (configure.py:989)

if "import(" in text or text.lstrip().startswith("import "):

The startswith("import ") arm only catches a bare import statement at the very beginning of the (whitespace-stripped) file. A static import appearing after the export const meta = {…} block — e.g., import foo from './lib' on line 20 — would not be caught. Only dynamic import() calls are caught everywhere. Since the runtime rejects both forms, the check underclaims coverage. Not an issue for the shipped spec-fanout.js (no imports), but worth noting before more workflow templates are added.

tigers1997 added a commit that referenced this pull request Aug 25, 2026
… line

Two defects in rule 4b, both raised as advisory findings on #95.

The meta block was located with text.split("}", 1)[0], which truncates at the
first nested brace. `phases: [{ title, detail }]` is a legitimate meta field, so
a workflow that wrote `phases:` ahead of `name:` would be reported as missing a
field it actually had. Replaced with a brace matcher that skips string literals,
so a brace inside a description no longer unbalances the scan either.

The static-import guard was text.lstrip().startswith("import "), which only
fires when `import` is the first token in the entire file. A mid-file
`import x from "y"` passed the check and then failed at invocation time, which
is exactly what this rule exists to prevent. Now matched at the start of any
line, with the dynamic-import test tightened so it doesn't fire on an
identifier that merely ends in "import".

Adds test/schema-hygiene/test-workflow-meta-rule.sh -- the rule had no test at
all. Covers both regressions, the string-brace case, a genuinely missing field,
an unterminated literal, and asserts the shipped spec-fanout.js stays clean.
@tigers1997

Copy link
Copy Markdown
Owner Author

Both advisories were real bugs, not style nits. Fixed in 93aaa46 rather than deferred — the rule was shipping in this PR, so it should be correct on arrival.

meta located by text.split("}", 1)[0]. Confirmed by reproduction before fixing: phases: [{ title, detail }] is a legitimate field, so the first } is a nested one. spec-fanout.js only passes because it happens to write name: and description: ahead of phases: — reorder those and the rule reports a missing field that is plainly there. Replaced with a brace matcher that also skips string literals, so a description: 'use {slot} syntax' can't unbalance the scan either.

text.lstrip().startswith("import "). Exactly as you described — only fires when import is the first token in the whole file. A mid-file import x from "y" passed the check and then failed at invocation time, which is the failure this rule exists to prevent. Now matched at the start of any line. I also tightened the dynamic test to (?<![\w$])import\s*\( so it doesn't fire on an identifier ending in import.

The rule had no test at all — that's the gap underneath both findings, and the more useful thing to close. Added test/schema-hygiene/test-workflow-meta-rule.sh: both regressions, the string-brace case, a genuinely missing field, an unterminated literal, a comment/identifier false-positive guard, and an assertion that the shipped workflow stays clean.

Verified the fix by running the old and new predicates side by side over each case — the two regressions flip from wrong to right, the previously-working cases stay working, and spec-fanout.js remains clean. No CHANGELOG change: the existing entry already claims the rule "validates every shipped workflow's meta block and uses no import()", which is now actually true.

@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: COMMENT-ONLY

All three changes are coherent and on-topic for the same audit batch. No correctness or license issues found. Two advisory notes below.

Blocking

None.

Advisory

1. Missing SchemaStore citation for workflowSizeGuideline and teammateMode

templates/core/dot-claude/settings.local.json.example and examples/python-uv-fastapi/.claude/settings.local.json.example both gain two new stubs:

"// workflowSizeGuideline": "medium",
"// teammateMode": "auto",

CONTRIBUTING.md requires citing SchemaStore when adding new keys to settings templates. These stubs use the //-prefix pattern and appear only in the .example file (which Claude Code does not parse), so the practical risk is near zero — but the process rule is to cite the source.

The compat survey entry already in ## Unreleased covers CC 2.1.183–2.1.241 with tested_up_to 2.1.220, and workflowSizeGuideline is CC 2.1.219+, which falls within that window. A one-line SchemaStore ref (e.g., the schema line or PR that introduced the key) in the CHANGELOG entry or the stub's -notes value would close this out.

2. Confirm all persona snapshots that include multi-agent are updated

Only examples/persona-small-team/expected-tree.txt gains the .claude/workflows/spec-fanout.js line. If any other persona scaffolds the multi-agent module, its snapshot needs the same addition — otherwise --check (which validates persona snapshots as part of the CI check job) will fail. The compat sync entry notes discipline-skills is in three personas; verify that multi-agent is only in small-team or update the others. The check gate will catch a real failure here, so this is purely a heads-up.

@tigers1997
tigers1997 force-pushed the fix/currency-corrections branch from d857a04 to c0e614a Compare August 25, 2026 16:05
tigers1997 added a commit that referenced this pull request Aug 25, 2026
… line

Two defects in rule 4b, both raised as advisory findings on #95.

The meta block was located with text.split("}", 1)[0], which truncates at the
first nested brace. `phases: [{ title, detail }]` is a legitimate meta field, so
a workflow that wrote `phases:` ahead of `name:` would be reported as missing a
field it actually had. Replaced with a brace matcher that skips string literals,
so a brace inside a description no longer unbalances the scan either.

The static-import guard was text.lstrip().startswith("import "), which only
fires when `import` is the first token in the entire file. A mid-file
`import x from "y"` passed the check and then failed at invocation time, which
is exactly what this rule exists to prevent. Now matched at the start of any
line, with the dynamic-import test tightened so it doesn't fire on an
identifier that merely ends in "import".

Adds test/schema-hygiene/test-workflow-meta-rule.sh -- the rule had no test at
all. Covers both regressions, the string-brace case, a genuinely missing field,
an unterminated literal, and asserts the shipped spec-fanout.js stays clean.
@tigers1997
tigers1997 force-pushed the feat/audit-open-items branch from 93aaa46 to e3c6009 Compare August 25, 2026 16:05
@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: PASS

Everything in scope is clean. Brief notes for the record:

INDEX.md generation — render_template_index() drives from MODULES and --check enforces it; no drift possible. The brace-matched _js_meta_block() is correct and the string-literal skip handles the {slot} regression case explicitly tested.

spec-fanout.js — export const meta is a pure literal with name:, description:, whenToUse:, and phases:; no import anywhere in the file; brace depth never goes negative. pipeline() (not parallel()) is used so screening starts as soon as each generator finishes — correct call.

workflow check (rule 4b) — the import guard covers any line via the multiline flag, plus a negative-lookbehind dynamic-import guard; test fixtures assert both. The two brace-truncation regression cases (phases-before-name; brace inside string) are explicitly covered.

Schema keys — workflowSizeGuideline and teammateMode land only in .claude/settings.local.json.example (the .example suffix means CC never parses it), so SchemaStore validation does not apply. Consistent with the established opt-in-stub pattern.

AGPL / MIT — no third-party code added; templates/discipline-skills/ untouched.

CHANGELOG — ## Unreleased entry is the first block, covers all three deliverables.

Minor observation (not blocking): the .ps1 extension added to the microbit-enforcer routing predicate in config_schema.py:48 has no corresponding template file in this PR — it is forward-looking dead code. Harmless, and CI --check would catch any mismatch when a .ps1 template is actually added.

@tigers1997
tigers1997 force-pushed the fix/currency-corrections branch from c0e614a to 62db375 Compare August 25, 2026 16:15
@tigers1997
tigers1997 changed the base branch from fix/currency-corrections to docs/mcp-rebaseline August 25, 2026 16:16
@tigers1997
tigers1997 force-pushed the docs/mcp-rebaseline branch from c0e614a to d0a2ae8 Compare August 25, 2026 16:21
tigers1997 added a commit that referenced this pull request Aug 25, 2026
… line

Two defects in rule 4b, both raised as advisory findings on #95.

The meta block was located with text.split("}", 1)[0], which truncates at the
first nested brace. `phases: [{ title, detail }]` is a legitimate meta field, so
a workflow that wrote `phases:` ahead of `name:` would be reported as missing a
field it actually had. Replaced with a brace matcher that skips string literals,
so a brace inside a description no longer unbalances the scan either.

The static-import guard was text.lstrip().startswith("import "), which only
fires when `import` is the first token in the entire file. A mid-file
`import x from "y"` passed the check and then failed at invocation time, which
is exactly what this rule exists to prevent. Now matched at the start of any
line, with the dynamic-import test tightened so it doesn't fire on an
identifier that merely ends in "import".

Adds test/schema-hygiene/test-workflow-meta-rule.sh -- the rule had no test at
all. Covers both regressions, the string-brace case, a genuinely missing field,
an unterminated literal, and asserts the shipped spec-fanout.js stays clean.
@tigers1997
tigers1997 force-pushed the feat/audit-open-items branch from e3c6009 to 23f2180 Compare August 25, 2026 16:21
@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: COMMENT-ONLY - Advisory findings only, nothing blocking. (1) ps1 routing residue in config_schema.py:48-51: adds .ps1 routing for microbit-enforcer but no .ps1 file ships in this PR, belonging to the stacked PowerShell PR -- dead code, no harm, soft-violates one-logical-change. (2) SchemaStore citation missing for workflowSizeGuideline and teammateMode stubs added to settings.local.json.example files -- risk low (comment-prefixed, example-only, cannot leak), but per established pattern a validation stamp note would close the loop before next tested_up_to bump. Everything else solid: _js_meta_block is brace-matched not split-on-first-brace, import guard covers mid-file statics and dynamic import(), new test covers both regression classes, persona snapshot updated, CHANGELOG entry present under Unreleased. No AGPL contamination; templates/discipline-skills/ untouched.

@tigers1997
tigers1997 force-pushed the docs/mcp-rebaseline branch from d0a2ae8 to a2b6c08 Compare August 25, 2026 17:31
tigers1997 and others added 2 commits August 25, 2026 13:34
…ent teams and routines

Three gaps the currency audit left open.

1. templates/INDEX.md was hand-maintained and had gone stale enough to
   mislead: it still referenced a configurator.html that no longer exists and
   was missing half the modules. It is now generated from MODULES by
   `python3 configure.py --write-index`, and --check fails when the committed
   copy and the generator disagree, so it cannot drift again.

2. Dynamic workflows have been a first-class Claude Code surface since
   2.1.154 and the configurator scaffolded nothing for them. The multi-agent
   module now ships .claude/workflows/spec-fanout.js, which runs as
   /spec-fanout: it generates N variants of one spec into disjoint slots, then
   screens each variant against the spec and the diversification axis before
   reporting. It is the workflow-native successor to the /infinite skill in
   the same module -- same job, but the runtime holds the loop and the
   intermediate results, the run is resumable, and the screening pass is a
   real gate rather than a suggestion. Project workflows under
   .claude/workflows/ are shared with everyone who clones the repo.

   --check gained a rule validating that every shipped workflow declares a
   usable `meta` block and uses no import(); the runtime rejects both, and a
   broken workflow would otherwise fail at invocation time in the user's
   project rather than at scaffold time here. workflowSizeGuideline is stubbed
   in settings.local.json.example.

3. docs/04 gains a table comparing the five ways to run work in parallel
   (subagent / skill / agent team / workflow / worktree session) by who holds
   the plan and where intermediate results live -- and states plainly why the
   configurator ships no templates for agent teams, channels or routines:

     - Teams are spawned in conversation and live for a session, so there is
       no project-level file that defines one. The only knob worth setting is
       teammateMode, a per-machine terminal preference, now stubbed in
       settings.local.
     - The channel gate keys channelsEnabled and allowedChannelPlugins are
       managed-settings only, so a project cannot enable them; an administrator
       does. Once enabled a channel is an MCP server like any other.
     - Routines are scheduled cloud agents that run against a repo rather than
       from your checkout, so a committed template has nothing to bind to. A
       Stop or SessionStart hook is the project-scoped equivalent, and the
       git-workflow module already ships one.

The microbit-enforcer routing rule in target_path_for is generalized from
".sh" to ".sh"/".ps1" so the directory's scripts route to .claude/hooks/ by
kind rather than by extension.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JVndNviHZSnbKJnWP7jFbV
… line

Two defects in rule 4b, both raised as advisory findings on #95.

The meta block was located with text.split("}", 1)[0], which truncates at the
first nested brace. `phases: [{ title, detail }]` is a legitimate meta field, so
a workflow that wrote `phases:` ahead of `name:` would be reported as missing a
field it actually had. Replaced with a brace matcher that skips string literals,
so a brace inside a description no longer unbalances the scan either.

The static-import guard was text.lstrip().startswith("import "), which only
fires when `import` is the first token in the entire file. A mid-file
`import x from "y"` passed the check and then failed at invocation time, which
is exactly what this rule exists to prevent. Now matched at the start of any
line, with the dynamic-import test tightened so it doesn't fire on an
identifier that merely ends in "import".

Adds test/schema-hygiene/test-workflow-meta-rule.sh -- the rule had no test at
all. Covers both regressions, the string-brace case, a genuinely missing field,
an unterminated literal, and asserts the shipped spec-fanout.js stays clean.
@tigers1997
tigers1997 force-pushed the feat/audit-open-items branch from 23f2180 to dfe8323 Compare August 25, 2026 17:35
@tigers1997
tigers1997 changed the base branch from docs/mcp-rebaseline to main August 25, 2026 17:35
@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: PASS

Clean PR. All three sub-changes address one theme (gaps in parallelism-surface coverage left by the currency audit), the CHANGELOG entry is well-formed under ## Unreleased, and no blocking criteria fire.

Spot-checks:

  • Correctness: _js_meta_block uses balanced-brace scanning (not split-on-first-}), and the test covers both regression classes the review note documents (nested phases: before name:, brace inside a string literal). The import-guard regex is multiline-anchored ((?m)^), catching mid-file statics and dynamic import(). spec-fanout.js uses pipeline() correctly — screener fires per variant as it completes, no barrier. Early-return on missing spec is safe. args || {} handles the no-args case.
  • Persona snapshots: small-team (the only persona that includes multi-agent) correctly gains ./.claude/workflows/spec-fanout.js; solo-experienced and solo-newer (neither includes multi-agent) are correctly unchanged.
  • AGPL / NOTICE: spec-fanout.js is original code in templates/multi-agent/; no third-party source, no NOTICE entry required.
  • templates/discipline-skills/ MIT integrity: no new files added to that subtree.
  • Schema-claim hygiene: workflowSizeGuideline and teammateMode are added as // stub entries in .example files only (CC never parses them), consistent with the established pattern from PR fix(schema-hygiene): retire //-stub pattern + drop unvalidatable skillOverrides #60. workflowSizeGuideline is CC 2.1.219+, inside the tested 2.1.116–2.1.220 envelope.
  • Scope: one logical change — three related gaps in the same currency audit.

@tigers1997 tigers1997 closed this Aug 25, 2026
@tigers1997 tigers1997 reopened this Aug 25, 2026
@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: COMMENT-ONLY

@claude

claude Bot commented Aug 25, 2026

Copy link
Copy Markdown

VERDICT: COMMENT-ONLY

Advisory

config_schema.py:1104-1108 - PS1 routing forward-reference belongs in the stacked PR

The change to target_path_for extending .endswith to include .ps1 is dead code in this PR. No .ps1 templates exist under templates/commands/microbit-enforcer/ in the current tree -- those were the PowerShell hooks explicitly split into the stacked PR. The change is harmless (it matches nothing, changes no behavior), but it is the one remnant of the scope that was split out. It would be cleaner to carry this 2-line routing prep in the stacked PowerShell PR alongside the templates it serves.

Everything else is clean: CHANGELOG entry present, no AGPL/license issues, discipline-skills/ untouched, persona snapshots correctly regenerated, the _js_meta_block brace-matcher handles nested objects and string-embedded braces correctly (verified by the new test fixtures in test/schema-hygiene/test-workflow-meta-rule.sh), the import() guard covers mid-file occurrences, and workflowSizeGuideline/teammateMode are confined to .local.json.example where the //-stub pattern is safe.

@tigers1997
tigers1997 merged commit bf7089c into main Aug 25, 2026
8 checks passed
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