From 98913547da2a7c539eb17737b1dc0e9a4fd04488 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:24:45 +0100 Subject: [PATCH] refactor: migrate repository documentation from Markdown to AsciiDoc --- CHANGELOG.adoc | 51 +++ CHANGELOG.md | 47 --- CODE_OF_CONDUCT.adoc | 339 ++++++++++++++++ CODE_OF_CONDUCT.md | 327 --------------- CONTRIBUTING.adoc | 20 + CONTRIBUTING.md | 20 - GOVERNANCE.adoc | 60 +++ GOVERNANCE.md | 60 --- README.adoc | 340 ++++++++++++++++ README.md | 372 ------------------ SECURITY.adoc | 16 + SECURITY.md | 18 - ...nch-protection-remediation-2026-04-10.adoc | 205 ++++++++++ ...ranch-protection-remediation-2026-04-10.md | 184 --------- docs/compliance-audit-2026-04-10.adoc | 347 ++++++++++++++++ docs/compliance-audit-2026-04-10.md | 263 ------------- docs/launcher-exceptions-2026-04-10.adoc | 260 ++++++++++++ docs/launcher-exceptions-2026-04-10.md | 157 -------- docs/ruleset-audit-2026-04-10/README.adoc | 122 ++++++ docs/ruleset-audit-2026-04-10/README.md | 99 ----- ...FER-VERIFICATION-CHECKLIST-2026-04-22.adoc | 99 +++++ ...NSFER-VERIFICATION-CHECKLIST-2026-04-22.md | 75 ---- docs/tech-debt-2026-05-26.adoc | 71 ++++ docs/tech-debt-2026-05-26.md | 57 --- examples/README.adoc | 30 ++ examples/README.md | 32 -- 26 files changed, 1960 insertions(+), 1711 deletions(-) create mode 100644 CHANGELOG.adoc delete mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.adoc delete mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.adoc delete mode 100644 CONTRIBUTING.md create mode 100644 GOVERNANCE.adoc delete mode 100644 GOVERNANCE.md create mode 100644 README.adoc delete mode 100644 README.md create mode 100644 SECURITY.adoc delete mode 100644 SECURITY.md create mode 100644 docs/branch-protection-remediation-2026-04-10.adoc delete mode 100644 docs/branch-protection-remediation-2026-04-10.md create mode 100644 docs/compliance-audit-2026-04-10.adoc delete mode 100644 docs/compliance-audit-2026-04-10.md create mode 100644 docs/launcher-exceptions-2026-04-10.adoc delete mode 100644 docs/launcher-exceptions-2026-04-10.md create mode 100644 docs/ruleset-audit-2026-04-10/README.adoc delete mode 100644 docs/ruleset-audit-2026-04-10/README.md create mode 100644 docs/ruleset-audit-2026-04-10/TRANSFER-VERIFICATION-CHECKLIST-2026-04-22.adoc delete mode 100644 docs/ruleset-audit-2026-04-10/TRANSFER-VERIFICATION-CHECKLIST-2026-04-22.md create mode 100644 docs/tech-debt-2026-05-26.adoc delete mode 100644 docs/tech-debt-2026-05-26.md create mode 100644 examples/README.adoc delete mode 100644 examples/README.md diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc new file mode 100644 index 0000000..33fccc2 --- /dev/null +++ b/CHANGELOG.adoc @@ -0,0 +1,51 @@ +== Changelog + +All notable changes to `+launch-scaffolder+` will be documented in this +file. + +This file is generated from conventional commits by the +https://github.com/hyperpolymath/standards/blob/main/.github/workflows/changelog-reusable.yml[`+changelog-reusable.yml+`] +workflow (`+hyperpolymath/standards#206+`). Adopt the workflow in this +repo’s CI to keep this file in sync automatically — see +https://github.com/hyperpolymath/standards/blob/main/templates/cliff.toml[`+templates/cliff.toml+`] +for the canonical config. + +The format follows https://keepachangelog.com/en/1.1.0/[Keep a +Changelog]; this project aims to follow +https://semver.org/spec/v2.0.0.html[Semantic Versioning]. + +=== [Unreleased] + +==== Added + +* feat(config): implement cmd_config (get/set/validate) +* feat(provision): native Rust integ/disinteg + shell-fallback +delegation +* feat(realign): implement cmd_realign + fixture-suffix convention +* feat(mint): wire end-to-end mint pipeline + migrate 7 launchers + +==== Fixed + +* fix(ci): sync hypatia-scan.yml to canonical (413: +env.HOME+Phase-2+SARIF) (#5) +* fix(ci): adopt canonical hypatia-scan.yml (env.HOME/scanner-layout + +Comment-step gate) (#3) + +==== Documentation + +* docs(readme): add SPDX header and/or standard badges +* docs(explainme): add EXPLAINME.adoc +* docs(compliance-audit): add frozen-snapshot banner +* docs: sweep for humans and machines + archive Wave 1 ruleset audit +* docs: add compliance audit and declared exceptions for .desktop-tools +launchers + +=== Pre-history + +Prior commits to this file’s introduction are recorded in git history +but not formally classified into Keep-a-Changelog sections. To backfill, +run `+git cliff -o CHANGELOG.md+` locally using the canonical +https://github.com/hyperpolymath/standards/blob/main/templates/cliff.toml[`+cliff.toml+`] +— this is one-shot mechanical work. + +''''' diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 32dd60f..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,47 +0,0 @@ - - -# Changelog - -All notable changes to `launch-scaffolder` will be documented in this file. - -This file is generated from conventional commits by the -[`changelog-reusable.yml`](https://github.com/hyperpolymath/standards/blob/main/.github/workflows/changelog-reusable.yml) -workflow (`hyperpolymath/standards#206`). Adopt the workflow in this repo's CI to keep this file in sync automatically — see -[`templates/cliff.toml`](https://github.com/hyperpolymath/standards/blob/main/templates/cliff.toml) -for the canonical config. - -The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); -this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -### Added - -- feat(config): implement cmd_config (get/set/validate) -- feat(provision): native Rust integ/disinteg + shell-fallback delegation -- feat(realign): implement cmd_realign + fixture-suffix convention -- feat(mint): wire end-to-end mint pipeline + migrate 7 launchers - -### Fixed - -- fix(ci): sync hypatia-scan.yml to canonical (413: env.HOME+Phase-2+SARIF) (#5) -- fix(ci): adopt canonical hypatia-scan.yml (env.HOME/scanner-layout + Comment-step gate) (#3) - -### Documentation - -- docs(readme): add SPDX header and/or standard badges -- docs(explainme): add EXPLAINME.adoc -- docs(compliance-audit): add frozen-snapshot banner -- docs: sweep for humans and machines + archive Wave 1 ruleset audit -- docs: add compliance audit and declared exceptions for .desktop-tools launchers - -## Pre-history - -Prior commits to this file's introduction are recorded in git history but not formally classified into Keep-a-Changelog sections. To backfill, run `git cliff -o CHANGELOG.md` locally using the canonical [`cliff.toml`](https://github.com/hyperpolymath/standards/blob/main/templates/cliff.toml) — this is one-shot mechanical work. - ---- - - diff --git a/CODE_OF_CONDUCT.adoc b/CODE_OF_CONDUCT.adoc new file mode 100644 index 0000000..ff3f3db --- /dev/null +++ b/CODE_OF_CONDUCT.adoc @@ -0,0 +1,339 @@ +== Code of Conduct + +=== Our Pledge + +We as members, contributors, and leaders pledge to make participation in +Squisher Corpus a harassment-free experience for everyone, regardless of +age, body size, visible or invisible disability, ethnicity, sex +characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, +race, caste, colour, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, +welcoming, diverse, inclusive, and healthy community. + +We recognise that a thriving open source community requires +*psychological safety* — an environment where people can contribute, ask +questions, make mistakes, and learn without fear of ridicule or +retaliation. + +''''' + +=== Our Standards + +==== Expected Behaviour + +The following behaviours contribute to a positive environment: + +*Communication* - Using welcoming and inclusive language - Being +respectful of differing viewpoints and experiences - Giving and +gracefully accepting constructive feedback - Assuming good intent while +addressing impact - Communicating clearly and patiently, especially with +newcomers + +*Collaboration* - Focusing on what is best for the community - Showing +empathy and kindness toward other community members - Being +collaborative rather than competitive - Mentoring and supporting less +experienced contributors - Celebrating others’ contributions and +successes + +*Professionalism* - Accepting responsibility and apologising to those +affected by our mistakes - Learning from the experience and avoiding +repetition - Respecting others’ time and attention - Staying on topic in +project spaces - Following project guidelines and conventions + +*Accessibility* - Using plain language and avoiding unnecessary jargon - +Providing alt text for images and transcripts for audio/video - Being +patient with those using assistive technologies - Accommodating +different communication styles and needs - Recognising that not everyone +communicates the same way + +==== Unacceptable Behaviour + +The following behaviours are considered harassment and are unacceptable: + +*Harassment* - The use of sexualised language or imagery, and sexual +attention or advances of any kind - Trolling, insulting or derogatory +comments, and personal or political attacks - Public or private +harassment - Deliberate intimidation, stalking, or following (online or +in-person) - Unwelcome physical contact or simulated physical contact +(e.g., emoji) - Sustained disruption of talks, events, or online +discussions + +*Discrimination* - Discriminatory jokes and language - Posting or +threatening to post others’ personally identifying information +("`doxing`") - Advocating for, or encouraging, any of the above +behaviour - Microaggressions — subtle, often unintentional, +discriminatory comments or actions + +*Professional Misconduct* - Publishing others’ private information +without explicit permission - Misrepresenting affiliation or +contributions - Plagiarism or claiming credit for others’ work - +Retaliating against anyone who reports a Code of Conduct violation - +Other conduct which could reasonably be considered inappropriate in a +professional setting + +==== Grey Areas + +Some situations require judgement. When uncertain: + +* *Intent vs Impact*: Good intentions do not excuse harmful impact. +Focus on making things right. +* *Power Dynamics*: Those with more power (maintainers, employers, +experienced contributors) must be especially mindful of their impact. +* *Cultural Differences*: What’s acceptable varies by culture. When in +doubt, err on the side of caution and ask. +* *Humour*: Jokes at others’ expense are rarely funny to everyone. Punch +up, not down. + +''''' + +=== Scope + +This Code of Conduct applies within all community spaces, including: + +*Online Spaces* - Repository discussions, issues, and pull/merge +requests - Project chat channels (Matrix, Discord, Slack, IRC) - Mailing +lists and forums - Social media when representing the project - Video +calls and virtual meetings + +*In-Person Spaces* - Conferences, meetups, and events - Workshops and +training sessions - Any gathering where you represent the project + +*Representation* This Code of Conduct also applies when an individual is +officially representing the community in public spaces. Examples +include: + +* Using an official project email address +* Posting via an official social media account +* Acting as an appointed representative at an event +* Speaking on behalf of the project + +''''' + +=== Enforcement + +==== Reporting + +If you experience or witness unacceptable behaviour, or have any other +concerns, please report it as soon as possible. + +*How to Report* + +[width="99%",cols="30%,33%,37%",options="header",] +|=== +|Method |Details |Best For +|*Email* |j.d.a.jewell@open.ac.uk |Detailed reports, sensitive matters + +|*Private Message* |Contact any maintainer directly |Quick questions, +minor issues + +|*Anonymous Form* |[Link to form if available] |When you need anonymity +|=== + +*What to Include* + +* Your contact information (unless anonymous) +* Names/usernames of those involved +* Description of what happened +* When and where it occurred +* Any witnesses +* Any supporting evidence (screenshots, links) +* How you would like us to respond (if you have a preference) + +*What Happens Next* + +[arabic] +. You will receive acknowledgment within *48 hours* +. The maintainers will review the report +. We may ask for additional information +. We will determine appropriate action +. We will inform you of the outcome (respecting others’ privacy) + +==== Confidentiality + +All reports will be handled with discretion: + +* Reporter identity is protected by default +* Details are shared only with those who need to know +* We will ask before naming you in any communication +* Anonymous reports are accepted and investigated + +==== Conflicts of Interest + +If a maintainers member is involved in an incident: + +* They will recuse themselves from the process +* Another maintainer or external party will handle the report +* We will disclose any potential conflicts + +''''' + +=== Enforcement Guidelines + +The maintainers will follow these guidelines in determining +consequences: + +==== 1. Correction + +*Community Impact*: Use of inappropriate language or other behaviour +deemed unprofessional or unwelcome. + +*Consequence*: A private, written warning providing clarity around the +nature of the violation and an explanation of why the behaviour was +inappropriate. A public apology may be requested. + +*Duration*: Immediate + +==== 2. Warning + +*Community Impact*: A violation through a single incident or series of +actions. + +*Consequence*: A warning with consequences for continued behaviour. No +interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, for a specified period. This +includes avoiding interactions in community spaces as well as external +channels like social media. Violating these terms may lead to a +temporary or permanent ban. + +*Duration*: 1-4 weeks + +==== 3. Temporary Ban + +*Community Impact*: A serious violation of community standards, +including sustained inappropriate behaviour. + +*Consequence*: A temporary ban from any sort of interaction or public +communication with the community for a specified period. No public or +private interaction with the people involved, including unsolicited +interaction with those enforcing the Code of Conduct, is allowed during +this period. Violating these terms may lead to a permanent ban. + +*Duration*: 1-6 months + +==== 4. Permanent Ban + +*Community Impact*: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behaviour, harassment of an +individual, or aggression toward or disparagement of classes of +individuals. + +*Consequence*: A permanent ban from any sort of public interaction +within the community. + +*Duration*: Permanent (with appeal rights after 12 months) + +==== Enforcement Across Perimeters + +For contributors with elevated access (Perimeter 2 or 1): + +[cols=",",options="header",] +|=== +|Level |Additional Consequence +|Correction |Noted in contributor record +|Warning |Access privileges may be temporarily reduced +|Temporary Ban |Access reduced to Perimeter 3 for ban duration +|Permanent Ban |All access revoked +|=== + +''''' + +=== Appeals + +If you believe an enforcement decision was made in error: + +[arabic] +. *Wait 7 days* after the decision (cooling-off period) +. *Email* j.d.a.jewell@open.ac.uk with subject line "`Appeal: [Original +Report ID]`" +. *Explain* why you believe the decision should be reconsidered +. *Provide* any new information not previously available + +*Appeals Process* + +* Appeals are reviewed by a different maintainers member than the +original +* You will receive a response within 14 days +* The appeals decision is final +* You may only appeal once per incident + +*Grounds for Appeal* + +* Procedural errors in the original investigation +* New evidence not previously available +* Disproportionate response to the violation +* Misunderstanding of facts + +''''' + +=== Supporting Those Who Report + +We are committed to supporting those who report violations: + +*We Will* - Believe and take all reports seriously - Respect your +privacy and confidentiality preferences - Keep you informed of progress +(if you wish) - Take steps to protect you from retaliation - Provide +resources if you need support + +*We Will Not* - Require you to confront the person directly - Dismiss +reports without investigation - Reveal your identity without consent - +Tolerate retaliation against reporters - Rush you to make decisions + +''''' + +=== Prevention + +Beyond enforcement, we actively work to prevent issues: + +*Onboarding* - All contributors are expected to read this Code of +Conduct - Perimeter 2 applicants must confirm they’ve read and +understood it - Maintainers receive additional training on enforcement + +*Culture* - We model the behaviour we expect - We intervene early when +we see potential issues - We thank people for positive contributions - +We create opportunities for diverse voices + +*Review* - This Code of Conduct is reviewed annually - Community +feedback is welcomed - Changes are communicated clearly + +''''' + +=== Acknowledgments + +This Code of Conduct is adapted from: + +* https://www.contributor-covenant.org/[Contributor Covenant], version +2.1 +* https://www.djangoproject.com/conduct/[Django Code of Conduct] +* https://www.rust-lang.org/policies/code-of-conduct[Rust Code of +Conduct] +* https://www.python.org/psf/conduct/[Python Community Code of Conduct] + +We thank these communities for their leadership in creating welcoming +spaces. + +''''' + +=== Questions? + +If you have questions about this Code of Conduct: + +* Open a +https://github.com/hyperpolymath/squisher-corpus/discussions[Discussion] +(for general questions) +* Email j.d.a.jewell@open.ac.uk (for private questions) +* Contact any maintainer directly + +''''' + +=== Summary + +*Be kind. Be respectful. Be collaborative.* + +We’re all here because we care about this project. Let’s make it a place +where everyone can do their best work. + +''''' + +Last updated: 2026 · Based on Contributor Covenant 2.1 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 8267cd4..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,327 +0,0 @@ -# Code of Conduct - - - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in Squisher Corpus a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, colour, religion, or sexual identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. - -We recognise that a thriving open source community requires **psychological safety** — an environment where people can contribute, ask questions, make mistakes, and learn without fear of ridicule or retaliation. - ---- - -## Our Standards - -### Expected Behaviour - -The following behaviours contribute to a positive environment: - -**Communication** -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Giving and gracefully accepting constructive feedback -- Assuming good intent while addressing impact -- Communicating clearly and patiently, especially with newcomers - -**Collaboration** -- Focusing on what is best for the community -- Showing empathy and kindness toward other community members -- Being collaborative rather than competitive -- Mentoring and supporting less experienced contributors -- Celebrating others' contributions and successes - -**Professionalism** -- Accepting responsibility and apologising to those affected by our mistakes -- Learning from the experience and avoiding repetition -- Respecting others' time and attention -- Staying on topic in project spaces -- Following project guidelines and conventions - -**Accessibility** -- Using plain language and avoiding unnecessary jargon -- Providing alt text for images and transcripts for audio/video -- Being patient with those using assistive technologies -- Accommodating different communication styles and needs -- Recognising that not everyone communicates the same way - -### Unacceptable Behaviour - -The following behaviours are considered harassment and are unacceptable: - -**Harassment** -- The use of sexualised language or imagery, and sexual attention or advances of any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Deliberate intimidation, stalking, or following (online or in-person) -- Unwelcome physical contact or simulated physical contact (e.g., emoji) -- Sustained disruption of talks, events, or online discussions - -**Discrimination** -- Discriminatory jokes and language -- Posting or threatening to post others' personally identifying information ("doxing") -- Advocating for, or encouraging, any of the above behaviour -- Microaggressions — subtle, often unintentional, discriminatory comments or actions - -**Professional Misconduct** -- Publishing others' private information without explicit permission -- Misrepresenting affiliation or contributions -- Plagiarism or claiming credit for others' work -- Retaliating against anyone who reports a Code of Conduct violation -- Other conduct which could reasonably be considered inappropriate in a professional setting - -### Grey Areas - -Some situations require judgement. When uncertain: - -- **Intent vs Impact**: Good intentions do not excuse harmful impact. Focus on making things right. -- **Power Dynamics**: Those with more power (maintainers, employers, experienced contributors) must be especially mindful of their impact. -- **Cultural Differences**: What's acceptable varies by culture. When in doubt, err on the side of caution and ask. -- **Humour**: Jokes at others' expense are rarely funny to everyone. Punch up, not down. - ---- - -## Scope - -This Code of Conduct applies within all community spaces, including: - -**Online Spaces** -- Repository discussions, issues, and pull/merge requests -- Project chat channels (Matrix, Discord, Slack, IRC) -- Mailing lists and forums -- Social media when representing the project -- Video calls and virtual meetings - -**In-Person Spaces** -- Conferences, meetups, and events -- Workshops and training sessions -- Any gathering where you represent the project - -**Representation** -This Code of Conduct also applies when an individual is officially representing the community in public spaces. Examples include: - -- Using an official project email address -- Posting via an official social media account -- Acting as an appointed representative at an event -- Speaking on behalf of the project - ---- - -## Enforcement - -### Reporting - -If you experience or witness unacceptable behaviour, or have any other concerns, please report it as soon as possible. - -**How to Report** - -| Method | Details | Best For | -|--------|---------|----------| -| **Email** | j.d.a.jewell@open.ac.uk | Detailed reports, sensitive matters | -| **Private Message** | Contact any maintainer directly | Quick questions, minor issues | -| **Anonymous Form** | [Link to form if available] | When you need anonymity | - -**What to Include** - -- Your contact information (unless anonymous) -- Names/usernames of those involved -- Description of what happened -- When and where it occurred -- Any witnesses -- Any supporting evidence (screenshots, links) -- How you would like us to respond (if you have a preference) - -**What Happens Next** - -1. You will receive acknowledgment within **48 hours** -2. The maintainers will review the report -3. We may ask for additional information -4. We will determine appropriate action -5. We will inform you of the outcome (respecting others' privacy) - -### Confidentiality - -All reports will be handled with discretion: - -- Reporter identity is protected by default -- Details are shared only with those who need to know -- We will ask before naming you in any communication -- Anonymous reports are accepted and investigated - -### Conflicts of Interest - -If a maintainers member is involved in an incident: - -- They will recuse themselves from the process -- Another maintainer or external party will handle the report -- We will disclose any potential conflicts - ---- - -## Enforcement Guidelines - -The maintainers will follow these guidelines in determining consequences: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behaviour deemed unprofessional or unwelcome. - -**Consequence**: A private, written warning providing clarity around the nature of the violation and an explanation of why the behaviour was inappropriate. A public apology may be requested. - -**Duration**: Immediate - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of actions. - -**Consequence**: A warning with consequences for continued behaviour. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. - -**Duration**: 1-4 weeks - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including sustained inappropriate behaviour. - -**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. - -**Duration**: 1-6 months - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behaviour, harassment of an individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the community. - -**Duration**: Permanent (with appeal rights after 12 months) - -### Enforcement Across Perimeters - -For contributors with elevated access (Perimeter 2 or 1): - -| Level | Additional Consequence | -|-------|----------------------| -| Correction | Noted in contributor record | -| Warning | Access privileges may be temporarily reduced | -| Temporary Ban | Access reduced to Perimeter 3 for ban duration | -| Permanent Ban | All access revoked | - ---- - -## Appeals - -If you believe an enforcement decision was made in error: - -1. **Wait 7 days** after the decision (cooling-off period) -2. **Email** j.d.a.jewell@open.ac.uk with subject line "Appeal: [Original Report ID]" -3. **Explain** why you believe the decision should be reconsidered -4. **Provide** any new information not previously available - -**Appeals Process** - -- Appeals are reviewed by a different maintainers member than the original -- You will receive a response within 14 days -- The appeals decision is final -- You may only appeal once per incident - -**Grounds for Appeal** - -- Procedural errors in the original investigation -- New evidence not previously available -- Disproportionate response to the violation -- Misunderstanding of facts - ---- - -## Supporting Those Who Report - -We are committed to supporting those who report violations: - -**We Will** -- Believe and take all reports seriously -- Respect your privacy and confidentiality preferences -- Keep you informed of progress (if you wish) -- Take steps to protect you from retaliation -- Provide resources if you need support - -**We Will Not** -- Require you to confront the person directly -- Dismiss reports without investigation -- Reveal your identity without consent -- Tolerate retaliation against reporters -- Rush you to make decisions - ---- - -## Prevention - -Beyond enforcement, we actively work to prevent issues: - -**Onboarding** -- All contributors are expected to read this Code of Conduct -- Perimeter 2 applicants must confirm they've read and understood it -- Maintainers receive additional training on enforcement - -**Culture** -- We model the behaviour we expect -- We intervene early when we see potential issues -- We thank people for positive contributions -- We create opportunities for diverse voices - -**Review** -- This Code of Conduct is reviewed annually -- Community feedback is welcomed -- Changes are communicated clearly - ---- - -## Acknowledgments - -This Code of Conduct is adapted from: - -- [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1 -- [Django Code of Conduct](https://www.djangoproject.com/conduct/) -- [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct) -- [Python Community Code of Conduct](https://www.python.org/psf/conduct/) - -We thank these communities for their leadership in creating welcoming spaces. - ---- - -## Questions? - -If you have questions about this Code of Conduct: - -- Open a [Discussion](https://github.com/hyperpolymath/squisher-corpus/discussions) (for general questions) -- Email j.d.a.jewell@open.ac.uk (for private questions) -- Contact any maintainer directly - ---- - -## Summary - -**Be kind. Be respectful. Be collaborative.** - -We're all here because we care about this project. Let's make it a place where everyone can do their best work. - ---- - -Last updated: 2026 · Based on Contributor Covenant 2.1 diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc new file mode 100644 index 0000000..0e9f4c1 --- /dev/null +++ b/CONTRIBUTING.adoc @@ -0,0 +1,20 @@ +== Contributing + +Contributions are welcome! Please: + +[arabic] +. Fork the repository +. Create a feature branch from `+main+` +. Ensure all CI checks pass +. Submit a pull request + +=== Standards + +This project follows the +https://github.com/hyperpolymath/standards[Rhodium Standard Repository +(RSR)] conventions. + +=== License + +By contributing, you agree that your contributions will be licensed +under MPL-2.0 (with MPL-2.0 as automatic legal fallback). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 166fe96..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,20 +0,0 @@ - -# Contributing - -Contributions are welcome! Please: - -1. Fork the repository -2. Create a feature branch from `main` -3. Ensure all CI checks pass -4. Submit a pull request - -## Standards - -This project follows the [Rhodium Standard Repository (RSR)](https://github.com/hyperpolymath/standards) conventions. - -## License - -By contributing, you agree that your contributions will be licensed under MPL-2.0 (with MPL-2.0 as automatic legal fallback). diff --git a/GOVERNANCE.adoc b/GOVERNANCE.adoc new file mode 100644 index 0000000..9b836fb --- /dev/null +++ b/GOVERNANCE.adoc @@ -0,0 +1,60 @@ +== Governance + +=== Overview + +This project is governed by the following principles and structures to +ensure transparent, inclusive, and effective decision-making. + +=== Roles and Responsibilities + +==== Maintainers + +Maintainers are responsible for: - Reviewing and merging pull requests - +Managing releases and versioning - Ensuring code quality and standards - +Triaging issues and bug reports - Community engagement and support + +==== Contributors + +Contributors are expected to: - Follow the code of conduct - Submit +well-documented pull requests - Write tests for new functionality - +Maintain existing tests - Update documentation as needed + +=== Decision Making + +==== Minor Changes + +* Can be made by any maintainer +* Include bug fixes, documentation updates, dependency updates + +==== Major Changes + +* Require discussion in issues or pull requests +* Include new features, architectural changes, API changes +* Need approval from at least 2 maintainers + +==== Breaking Changes + +* Require RFC (Request for Comments) process +* Need approval from majority of maintainers +* Must include migration guide + +=== Code of Conduct + +All participants are expected to follow our Code of Conduct. Violations +can be reported to the maintainers. + +=== Communication + +* *Issues*: For bug reports and feature requests +* *Discussions*: For questions and general discussion +* *Pull Requests*: For code contributions + +=== Licensing + +All contributions are made under the terms of the repository’s LICENSE +file. By submitting a pull request, you agree to license your +contributions accordingly. + +''''' + +_Last updated: 2026-07-18_ diff --git a/GOVERNANCE.md b/GOVERNANCE.md deleted file mode 100644 index e27364c..0000000 --- a/GOVERNANCE.md +++ /dev/null @@ -1,60 +0,0 @@ -# Governance - -## Overview - -This project is governed by the following principles and structures to ensure transparent, inclusive, and effective decision-making. - -## Roles and Responsibilities - -### Maintainers - -Maintainers are responsible for: -- Reviewing and merging pull requests -- Managing releases and versioning -- Ensuring code quality and standards -- Triaging issues and bug reports -- Community engagement and support - -### Contributors - -Contributors are expected to: -- Follow the code of conduct -- Submit well-documented pull requests -- Write tests for new functionality -- Maintain existing tests -- Update documentation as needed - -## Decision Making - -### Minor Changes -- Can be made by any maintainer -- Include bug fixes, documentation updates, dependency updates - -### Major Changes -- Require discussion in issues or pull requests -- Include new features, architectural changes, API changes -- Need approval from at least 2 maintainers - -### Breaking Changes -- Require RFC (Request for Comments) process -- Need approval from majority of maintainers -- Must include migration guide - -## Code of Conduct - -All participants are expected to follow our Code of Conduct. Violations can be reported to the maintainers. - -## Communication - -- **Issues**: For bug reports and feature requests -- **Discussions**: For questions and general discussion -- **Pull Requests**: For code contributions - -## Licensing - -All contributions are made under the terms of the repository's LICENSE file. -By submitting a pull request, you agree to license your contributions accordingly. - ---- - -*Last updated: 2026-07-18* diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..0cf7779 --- /dev/null +++ b/README.adoc @@ -0,0 +1,340 @@ +*Build cross-platform desktop launchers from a declarative A2ML spec.* + +https://www.bestpractices.dev/en/projects/new?repo_url=https://github.com/hyperpolymath/launch-scaffolder[image:https://img.shields.io/badge/OpenSSF-Best_Practices-green?logo=opensourcesecurity[OpenSSF +Best Practices]] +https://opensource.org/licenses/MPL-2.0[image:https://img.shields.io/badge/License-MPL_2.0-blue.svg[License: +MPL-2.0]] +image:https://img.shields.io/badge/rust-1.85%2B-orange.svg[Rust 1.85+] +image:https://img.shields.io/badge/status-alpha-yellow.svg[Status: +alpha] image:https://img.shields.io/badge/mint-working-green.svg[Mint: +working] +image:https://img.shields.io/badge/realign-working-green.svg[Realign: +working] +image:https://img.shields.io/badge/provision-working-green.svg[Provision: +working] +image:https://img.shields.io/badge/config-working-green.svg[Config: +working] + +== What it does + +`+launch-scaffolder+` is a single Rust binary that generates, installs, +and maintains cross-platform desktop launcher scripts for any +hyperpolymath project (or any other project that adopts the +hyperpolymath launcher standard). It turns launcher scripts into +*generated artefacts*, not hand-edited files. + +Inputs: + +* `+launcher-standard.a2ml+` — a declarative description of what a +compliant launcher looks like (modes, file paths, permissions, integrity +requirements, per-platform behaviour). The canonical copy lives in the +`+standards+` monorepo at +`+/var/mnt/eclipse/repos/developer-ecosystem/standards/launcher/launcher-standard.a2ml+`. +At runtime the loader resolves in this order: +[arabic] +. `+--standard+` `++` CLI flag +. `+$LAUNCH_SCAFFOLDER_STANDARD+` environment variable +. The canonical path above +. A baked-in copy compiled into the binary at build time (fallback only) +* `+.launcher.a2ml+` — per-app config (name, display, URL, command, +repo, icon, and an optional `+[exceptions]+` block). + +Output: + +* `+-launcher.sh+` — a fully spec-compliant cross-platform launcher +(Linux / macOS / Windows-via-Git-Bash). + +== Why this exists + +Without this tool, every hyperpolymath project maintains its own +hand-written `+~600-line+` `+bash+` `+launcher+` `+script+` that has to +be kept in sync with the current launcher standard by hand. In practice, +drift is guaranteed. The launcher standard evolves; the launchers don’t. +Fixing a spec issue means editing 11+ files and hoping you caught them +all. + +With this tool: + +* The standard is a single file. Changing it is a one-line edit. +* Every launcher is regenerated from the same standard + its own config. +* Bulk realignment after a spec change is one command: +`+launch-scaffolder+` `+realign+` `+*.launcher.a2ml+`. +* Launchers become reproducible, auditable artefacts — not hand-edited +drift. +* "`Not wasting tokens in future`" — future AI sessions read one spec +file, not 11 near-identical launcher scripts. + +== Commands + +All four working subcommands, in the order you’re likely to use them: + +[source,bash] +---- +# ─── mint ────────────────────────────────────────────────────────────────── +# Generate a launcher script from a config. Writes +# /-launcher.sh by default. +launch-scaffolder mint examples/stapeln.launcher.fixture.a2ml +launch-scaffolder mint /path/to/burble.launcher.a2ml -o /tmp/out.sh +launch-scaffolder mint /path/to/burble.launcher.a2ml --stdout + +# ─── realign ─────────────────────────────────────────────────────────────── +# Bulk re-mint: walk the estate (default = /var/mnt/eclipse/repos) and +# re-render every live `.launcher.a2ml` against the current standard +# + template. Idempotent — unchanged scripts stay unchanged. +launch-scaffolder realign # walk estate root +launch-scaffolder realign --search-root ~/my-projects # narrow walk +launch-scaffolder realign /path/to/one.launcher.a2ml # explicit configs +launch-scaffolder realign --dry-run # preview only +launch-scaffolder realign --check # CI mode: exit 1 on any diff +launch-scaffolder realign --keep-going # don't stop on errors + +# ─── provision ───────────────────────────────────────────────────────────── +# Install (--integ) or uninstall (--disinteg) a launcher's desktop entry, +# icon, and launcher binary on the current system. Writes .desktop files +# directly from the Rust binary rather than going via the generated shell +# script. Bulk runs (--all) prompt before touching $HOME. +launch-scaffolder provision --integ /path/to/burble.launcher.a2ml +launch-scaffolder provision --disinteg /path/to/burble.launcher.a2ml +launch-scaffolder provision --integ --all # everything in the estate +launch-scaffolder provision --integ --all --no-confirm --force +launch-scaffolder provision --integ --all --dry-run # preview only + +# ─── config ──────────────────────────────────────────────────────────────── +# Inspect or edit the `@a2ml-metadata` block embedded at the top of any +# generated launcher script. +launch-scaffolder config get ./stapeln-launcher.sh version +launch-scaffolder config get ./stapeln-launcher.sh standards-compliance +launch-scaffolder config validate ./stapeln-launcher.sh +launch-scaffolder config set ./stapeln-launcher.sh version 0.2.0 +# NOTE: `config set` rewrites the generated script in place and warns +# that `realign` will overwrite the change. The durable fix is to edit +# the source .launcher.a2ml and re-mint. +---- + +=== Generated script → binary delegation + +Generated `+-launcher.sh+` scripts carry their own `+--integ+` / +`+--disinteg+` arms (the original, pure-bash implementation). They now +also embed the absolute path of their source config as `+CONFIG_FILE=…+` +and, on invocation, fast-path back to `+launch-scaffolder+` +`+provision+` `+--integ+` `+"$CONFIG_FILE"+` when the binary is on +`+PATH+`. If the binary isn’t present, the in-script shell fallback runs +instead. This means: the Rust binary is authoritative *when available*, +without making itself a hard dependency for integrated launchers. + +== Architecture + +.... +launch-scaffolder/ +├── Cargo.toml # Workspace root +├── crates/ +│ ├── launcher-common/ # Shared library — all real logic +│ │ └── src/ +│ │ ├── lib.rs # Public API +│ │ ├── standard.rs # Parse launcher-standard.a2ml +│ │ │ # + LauncherStandard::resolve (shared +│ │ │ # 3-step precedence: flag → canonical +│ │ │ # → baked fallback) +│ │ ├── config.rs # Parse .launcher.a2ml +│ │ ├── template.rs # Render via Tera (embeds CONFIG_FILE) +│ │ ├── discovery.rs # Walk + prune + fixture-suffix filter +│ │ │ # (shared by realign + provision) +│ │ ├── integration.rs # Native Rust .desktop writer, +│ │ │ # icon/launcher install, gio, +│ │ │ # update-desktop-database +│ │ ├── metadata_block.rs # Parser + in-place rewriter for the +│ │ │ # embedded @a2ml-metadata block +│ │ ├── platform.rs # Linux/macOS/Windows dispatch (stub) +│ │ ├── integrity.rs # SHA-256 manifests (stub) +│ │ └── exceptions.rs # Standard + config + exception merge (stub) +│ └── launcher/ # Thin CLI binary +│ └── src/ +│ ├── main.rs # clap dispatch +│ ├── cmd_mint.rs # ✓ working +│ ├── cmd_realign.rs # ✓ working +│ ├── cmd_provision.rs # ✓ working (native Rust; option b) +│ ├── cmd_config.rs # ✓ working +│ └── cmd_standard.rs # stub +├── standards/ +│ └── launcher-standard.a2ml # Canonical standard (baked into binary) +├── templates/ +│ └── launcher.sh.tera # Bash launcher template rendered by Tera +├── examples/ +│ ├── README.md # Fixture-vs-live naming convention +│ └── stapeln.launcher.fixture.a2ml # Worked example (fixture suffix!) +├── docs/ +│ ├── launcher-exceptions-2026-04-10.md +│ ├── compliance-audit-2026-04-10.md +│ ├── branch-protection-remediation-2026-04-10.md +│ └── ruleset-audit-2026-04-10/ # Audit artefacts (read-only record) +└── tests/ + └── regression/ # Golden-file regression fixtures (planned) +.... + +=== Fixture-vs-live naming convention + +To distinguish test fixtures from live, estate-owned launcher configs, +*file-name suffixes carry the distinction* — directory names do not: + +[cols=",,",options="header",] +|=== +|Purpose |File-name suffix |Picked up by estate walks? +|Live per-app config |`+.launcher.a2ml+` |*Yes* +|Fixture / worked example |`+.launcher.fixture.a2ml+` |*No* +|=== + +The discovery code in `launch-scaffolder-common + +discovery + +is_live_config` enforces this rule. Fixture files can live anywhere — +including inside a consumer repo’s `+examples/+` directory — without +being swept up by `+realign+` or `+provision+` `+--all+`. See +`+examples/README.md+` for the full contributor-facing version of the +rule. + +=== Why Rust/SPARK + +Per the hyperpolymath language policy (see +`+standards/rhodium-standard-repositories/spec/LANGUAGE-POLICY.adoc+`): + +* *Rust* is the preferred language for CLI tools — zero-dep binary, fast +cold start, strong types, excellent ecosystem (clap, tera, sha2, +anyhow). +* *"`Rust`" always means "`Rust with SPARK integration as the default +stance`"* — this tool is Rust-primary now, with SPARK/Ada hooks planned +for the correctness-critical `+integrity.rs+` path (called via Zig FFI +per the hyperpolymath ABI/FFI standard). + +=== Why A2ML for inputs + +* A2ML is the hyperpolymath standard format for machine-readable config +and metadata, and every launcher already carries an A2ML metadata block +in its header. Using A2ML end-to-end means a launcher script can be +*parsed by this tool* to extract its config and re-minted. +* v0.1 uses TOML as the concrete syntax (A2ML is "`TOML-like`" per the +standards `+.claude/CLAUDE.md+`). v0.2 switches to proper A2ML once the +`+a2ml-rs+` parser reaches feature parity. + +== Status + +*Alpha, ~65% complete. Four of five subcommands fully wired end-to-end; +one remains a stub (`+standard+`). Last updated 2026-04-10 (phase +`+phase-4-config-inspector+`).* + +Implemented: + +* [x] Cargo workspace layout (two crates: `+launch-scaffolder-common+` + +`+launch-scaffolder+`) ++ +* [x] `+standard+` module — parses `+launcher-standard.a2ml+`; +`LauncherStandard + +resolve` hosts the shared 3-step precedence (flag → canonical → baked) +* [x] `+config+` module — parses `+.launcher.a2ml+` (TOML) with +runtime-kind validation +* [x] `+template+` module — Tera renderer with full context; embeds +`+CONFIG_FILE+` for in-script delegation +* [x] `+discovery+` module — shared walk + prune + fixture-suffix filter ++ +* [x] `+integration+` module — native Rust `+.desktop+` writer, +icon/launcher install, best-effort `+gio+` + `+update-desktop-database+` +(Linux only; macOS/Windows return `IntegError + +UnsupportedPlatform`) +* [x] `+metadata_block+` module — hand-rolled parser and in-place +rewriter for the embedded `+@a2ml-metadata+` block +* [x] `+templates/launcher.sh.tera+` — parameterised over +`+runtime_kind+` `+∈+` `+{server-url,+` `+process,+` `+remote}+`; +`+--integ+` / `+--disinteg+` fast-path to `+launch-scaffolder+` +`+provision+` when on PATH +* [x] *`+mint+`* subcommand — positional config, `+-o/--out+`, +`+--stdout+`, `+--no-chmod+` +* [x] *`+realign+`* subcommand — estate walk with `+--search-root+` +override, `+--dry-run+`, `+--check+` (CI), `+--keep-going+`, walk-error +tolerant +* [x] *`+provision+`* subcommand — `+--integ+` / `+--disinteg+` with +`+--all+`, `+--force+`, `+--no-confirm+`, `+--dry-run+`; bulk mode +prompts before touching `+$HOME+` +* [x] *`+config+`* subcommand — `+get+` / `+set+` / `+validate+`; +`+set+` preserves column alignment in the embedded metadata block +* [x] 17 unit tests passing across both crates +* [x] 7 launchers fully managed: aerie, burble, game-server-admin, nqc, +panll, project-wharf, stapeln +* [x] 5 declared exceptions documented in +`+docs/launcher-exceptions-2026-04-10.md+` +* [x] Fixture-vs-live file-naming convention documented and enforced +* [x] All 7 managed launchers regenerated with template delegation arms +(2026-04-10) + +Remaining work (ordered by current priority in STATE.a2ml): + +* [ ] Golden-file regression tests pinning mint output for the 7 managed +launchers ++ +* [ ] macOS integration backend in `launch-scaffolder-common + +integration` +* [ ] SPARK integration hook for `+integrity.rs+` via Zig FFI +* [ ] `+standard+` subcommand — still a scaffold stub +* [ ] `+platform+` module — still a stub (runtime dispatch handled +inside the generated script today) +* [ ] `+integrity+` module — still a stub (pending SHA-256 manifest +generator) +* [ ] `+exceptions+` module — still a stub (per-app override merge) +* [ ] Cross-platform CI (Linux/macOS/Windows matrix) +* [ ] Migration of the 5 declared exceptions once the template grows +custom-mode hooks + +== Build + +[source,bash] +---- +# Standard cargo workflow +cargo build # debug build +cargo build --release # optimized, stripped, single-file binary +cargo test # run tests +cargo run -- --help # invoke the binary + +# Or via Justfile +just build +just test +just install # cargo install --path crates/launcher +---- + +== License + +This project is licensed under the Mozilla Public License, v. 2.0. See +the `+LICENSE+` file for details. + +SPDX-License-Identifier: CC-BY-SA-4.0 + +== Author + +Jonathan D.A. Jewell (hyperpolymath) + +j.d.a.jewell@open.ac.uk + +== Relationship to other hyperpolymath projects + +Each consumer repo owns its own `+.launcher.a2ml+` config at its +repository root. `+launch-scaffolder+` `+mint+` writes the generated +`+-launcher.sh+` next to it. The pre-2026-04-10 pattern of pooling +launchers under `+/var/mnt/eclipse/repos/.desktop-tools/+` is +deprecated; `+~/Desktop/Shortcuts/*.desktop+` files have been repointed +at the new per-repo paths. + +*Scaffolder-managed consumers (as of 2026-04-10):* + +* *stapeln* — Visual Container Stack Designer (server-url, port 4010) +* *burble* — WebRTC voice + control plane (server-url, port 4020) +* *aerie* — network diagnostic suite (process) +* *game-server-admin* — multi-game server orchestration (process) +* *nqc* — NextGen Query Client, inside `+nextgen-databases/+` (process) +* *panll* — panels framework (server-url, port 8000) +* *project-wharf* — container/workload staging (process) + +*Declared exceptions* (still hand-written; see +`+docs/launcher-exceptions-2026-04-10.md+` for reasoning and migration +triggers): + +* `+hypatia+`, `+invariant-path+`, `+opsm+`, `+ambientops+`, `+idaptik+` + +The bulk-realignment goal: once `+realign+` is implemented, one command +will re-mint every managed launcher in the estate against the current +standard. diff --git a/README.md b/README.md deleted file mode 100644 index fb923e5..0000000 --- a/README.md +++ /dev/null @@ -1,372 +0,0 @@ - - -**Build cross-platform desktop launchers from a declarative A2ML spec.** - -[![OpenSSF Best Practices](https://img.shields.io/badge/OpenSSF-Best_Practices-green?logo=opensourcesecurity)](https://www.bestpractices.dev/en/projects/new?repo_url=https://github.com/hyperpolymath/launch-scaffolder) -[![License: MPL-2.0](https://img.shields.io/badge/License-MPL_2.0-blue.svg)](https://opensource.org/licenses/MPL-2.0) -![Rust 1.85+](https://img.shields.io/badge/rust-1.85%2B-orange.svg) -![Status: alpha](https://img.shields.io/badge/status-alpha-yellow.svg) -![Mint: working](https://img.shields.io/badge/mint-working-green.svg) -![Realign: -working](https://img.shields.io/badge/realign-working-green.svg) -![Provision: -working](https://img.shields.io/badge/provision-working-green.svg) -![Config: -working](https://img.shields.io/badge/config-working-green.svg) - -# What it does - -`launch-scaffolder` is a single Rust binary that generates, installs, -and maintains cross-platform desktop launcher scripts for any -hyperpolymath project (or any other project that adopts the -hyperpolymath launcher standard). It turns launcher scripts into -**generated artefacts**, not hand-edited files. - -Inputs: - -- `launcher-standard.a2ml` — a declarative description of what a - compliant launcher looks like (modes, file paths, permissions, - integrity requirements, per-platform behaviour). The canonical copy - lives in the `standards` monorepo at - `/var/mnt/eclipse/repos/developer-ecosystem/standards/launcher/launcher-standard.a2ml`. - At runtime the loader resolves in this order: - - 1. `--standard` `` CLI flag - - 2. `$LAUNCH_SCAFFOLDER_STANDARD` environment variable - - 3. The canonical path above - - 4. A baked-in copy compiled into the binary at build time (fallback - only) - -- `.launcher.a2ml` — per-app config (name, display, URL, command, - repo, icon, and an optional `[exceptions]` block). - -Output: - -- `-launcher.sh` — a fully spec-compliant cross-platform launcher - (Linux / macOS / Windows-via-Git-Bash). - -# Why this exists - -Without this tool, every hyperpolymath project maintains its own -hand-written `~600-line` `bash` `launcher` `script` that has to be kept -in sync with the current launcher standard by hand. In practice, drift -is guaranteed. The launcher standard evolves; the launchers don’t. -Fixing a spec issue means editing 11+ files and hoping you caught them -all. - -With this tool: - -- The standard is a single file. Changing it is a one-line edit. - -- Every launcher is regenerated from the same standard + its own config. - -- Bulk realignment after a spec change is one command: - `launch-scaffolder` `realign` `*.launcher.a2ml`. - -- Launchers become reproducible, auditable artefacts — not hand-edited - drift. - -- "Not wasting tokens in future" — future AI sessions read one spec - file, not 11 near-identical launcher scripts. - -# Commands - -All four working subcommands, in the order you’re likely to use them: - -```bash -# ─── mint ────────────────────────────────────────────────────────────────── -# Generate a launcher script from a config. Writes -# /-launcher.sh by default. -launch-scaffolder mint examples/stapeln.launcher.fixture.a2ml -launch-scaffolder mint /path/to/burble.launcher.a2ml -o /tmp/out.sh -launch-scaffolder mint /path/to/burble.launcher.a2ml --stdout - -# ─── realign ─────────────────────────────────────────────────────────────── -# Bulk re-mint: walk the estate (default = /var/mnt/eclipse/repos) and -# re-render every live `.launcher.a2ml` against the current standard -# + template. Idempotent — unchanged scripts stay unchanged. -launch-scaffolder realign # walk estate root -launch-scaffolder realign --search-root ~/my-projects # narrow walk -launch-scaffolder realign /path/to/one.launcher.a2ml # explicit configs -launch-scaffolder realign --dry-run # preview only -launch-scaffolder realign --check # CI mode: exit 1 on any diff -launch-scaffolder realign --keep-going # don't stop on errors - -# ─── provision ───────────────────────────────────────────────────────────── -# Install (--integ) or uninstall (--disinteg) a launcher's desktop entry, -# icon, and launcher binary on the current system. Writes .desktop files -# directly from the Rust binary rather than going via the generated shell -# script. Bulk runs (--all) prompt before touching $HOME. -launch-scaffolder provision --integ /path/to/burble.launcher.a2ml -launch-scaffolder provision --disinteg /path/to/burble.launcher.a2ml -launch-scaffolder provision --integ --all # everything in the estate -launch-scaffolder provision --integ --all --no-confirm --force -launch-scaffolder provision --integ --all --dry-run # preview only - -# ─── config ──────────────────────────────────────────────────────────────── -# Inspect or edit the `@a2ml-metadata` block embedded at the top of any -# generated launcher script. -launch-scaffolder config get ./stapeln-launcher.sh version -launch-scaffolder config get ./stapeln-launcher.sh standards-compliance -launch-scaffolder config validate ./stapeln-launcher.sh -launch-scaffolder config set ./stapeln-launcher.sh version 0.2.0 -# NOTE: `config set` rewrites the generated script in place and warns -# that `realign` will overwrite the change. The durable fix is to edit -# the source .launcher.a2ml and re-mint. -``` - -## Generated script → binary delegation - -Generated `-launcher.sh` scripts carry their own `--integ` / -`--disinteg` arms (the original, pure-bash implementation). They now -also embed the absolute path of their source config as `CONFIG_FILE=…` -and, on invocation, fast-path back to `launch-scaffolder` `provision` -`--integ` `"$CONFIG_FILE"` when the binary is on `PATH`. If the binary -isn’t present, the in-script shell fallback runs instead. This means: -the Rust binary is authoritative **when available**, without making -itself a hard dependency for integrated launchers. - -# Architecture - - launch-scaffolder/ - ├── Cargo.toml # Workspace root - ├── crates/ - │ ├── launcher-common/ # Shared library — all real logic - │ │ └── src/ - │ │ ├── lib.rs # Public API - │ │ ├── standard.rs # Parse launcher-standard.a2ml - │ │ │ # + LauncherStandard::resolve (shared - │ │ │ # 3-step precedence: flag → canonical - │ │ │ # → baked fallback) - │ │ ├── config.rs # Parse .launcher.a2ml - │ │ ├── template.rs # Render via Tera (embeds CONFIG_FILE) - │ │ ├── discovery.rs # Walk + prune + fixture-suffix filter - │ │ │ # (shared by realign + provision) - │ │ ├── integration.rs # Native Rust .desktop writer, - │ │ │ # icon/launcher install, gio, - │ │ │ # update-desktop-database - │ │ ├── metadata_block.rs # Parser + in-place rewriter for the - │ │ │ # embedded @a2ml-metadata block - │ │ ├── platform.rs # Linux/macOS/Windows dispatch (stub) - │ │ ├── integrity.rs # SHA-256 manifests (stub) - │ │ └── exceptions.rs # Standard + config + exception merge (stub) - │ └── launcher/ # Thin CLI binary - │ └── src/ - │ ├── main.rs # clap dispatch - │ ├── cmd_mint.rs # ✓ working - │ ├── cmd_realign.rs # ✓ working - │ ├── cmd_provision.rs # ✓ working (native Rust; option b) - │ ├── cmd_config.rs # ✓ working - │ └── cmd_standard.rs # stub - ├── standards/ - │ └── launcher-standard.a2ml # Canonical standard (baked into binary) - ├── templates/ - │ └── launcher.sh.tera # Bash launcher template rendered by Tera - ├── examples/ - │ ├── README.md # Fixture-vs-live naming convention - │ └── stapeln.launcher.fixture.a2ml # Worked example (fixture suffix!) - ├── docs/ - │ ├── launcher-exceptions-2026-04-10.md - │ ├── compliance-audit-2026-04-10.md - │ ├── branch-protection-remediation-2026-04-10.md - │ └── ruleset-audit-2026-04-10/ # Audit artefacts (read-only record) - └── tests/ - └── regression/ # Golden-file regression fixtures (planned) - -## Fixture-vs-live naming convention - -To distinguish test fixtures from live, estate-owned launcher configs, -**file-name suffixes carry the distinction** — directory names do not: - -| Purpose | File-name suffix | Picked up by estate walks? | -|----|----|----| -| Live per-app config | `.launcher.a2ml` | **Yes** | -| Fixture / worked example | `.launcher.fixture.a2ml` | **No** | - -The discovery code in \`launch-scaffolder-common - -discovery -is_live_config\` enforces this rule. Fixture files can live anywhere — -including inside a consumer repo’s `examples/` directory — without being -swept up by `realign` or `provision` `--all`. See `examples/README.md` -for the full contributor-facing version of the rule. - -## Why Rust/SPARK - -Per the hyperpolymath language policy (see -`standards/rhodium-standard-repositories/spec/LANGUAGE-POLICY.adoc`): - -- **Rust** is the preferred language for CLI tools — zero-dep binary, - fast cold start, strong types, excellent ecosystem (clap, tera, sha2, - anyhow). - -- **"Rust" always means "Rust with SPARK integration as the default - stance"** — this tool is Rust-primary now, with SPARK/Ada hooks - planned for the correctness-critical `integrity.rs` path (called via - Zig FFI per the hyperpolymath ABI/FFI standard). - -## Why A2ML for inputs - -- A2ML is the hyperpolymath standard format for machine-readable config - and metadata, and every launcher already carries an A2ML metadata - block in its header. Using A2ML end-to-end means a launcher script can - be **parsed by this tool** to extract its config and re-minted. - -- v0.1 uses TOML as the concrete syntax (A2ML is "TOML-like" per the - standards `.claude/CLAUDE.md`). v0.2 switches to proper A2ML once the - `a2ml-rs` parser reaches feature parity. - -# Status - -**Alpha, ~65% complete. Four of five subcommands fully wired end-to-end; -one remains a stub (`standard`). Last updated 2026-04-10 (phase -`phase-4-config-inspector`).** - -Implemented: - -- [x] Cargo workspace layout (two crates: `launch-scaffolder-common` + - `launch-scaffolder`) - - \* \[x\] `standard` module — parses `launcher-standard.a2ml`; \`LauncherStandard - resolve\` hosts the shared 3-step precedence (flag → canonical → - baked) - -- [x] `config` module — parses `.launcher.a2ml` (TOML) with - runtime-kind validation - -- [x] `template` module — Tera renderer with full context; embeds - `CONFIG_FILE` for in-script delegation - -- [x] `discovery` module — shared walk + prune + fixture-suffix filter - - \* \[x\] `integration` module — native Rust `.desktop` writer, icon/launcher install, best-effort `gio` + `update-desktop-database` (Linux only; macOS/Windows return \`IntegError - UnsupportedPlatform\`) - -- [x] `metadata_block` module — hand-rolled parser and in-place rewriter - for the embedded `@a2ml-metadata` block - -- [x] `templates/launcher.sh.tera` — parameterised over `runtime_kind` - `∈` `{server-url,` `process,` `remote}`; `--integ` / `--disinteg` - fast-path to `launch-scaffolder` `provision` when on PATH - -- [x] **`mint`** subcommand — positional config, `-o/--out`, `--stdout`, - `--no-chmod` - -- [x] **`realign`** subcommand — estate walk with `--search-root` - override, `--dry-run`, `--check` (CI), `--keep-going`, walk-error - tolerant - -- [x] **`provision`** subcommand — `--integ` / `--disinteg` with - `--all`, `--force`, `--no-confirm`, `--dry-run`; bulk mode prompts - before touching `$HOME` - -- [x] **`config`** subcommand — `get` / `set` / `validate`; `set` - preserves column alignment in the embedded metadata block - -- [x] 17 unit tests passing across both crates - -- [x] 7 launchers fully managed: aerie, burble, game-server-admin, nqc, - panll, project-wharf, stapeln - -- [x] 5 declared exceptions documented in - `docs/launcher-exceptions-2026-04-10.md` - -- [x] Fixture-vs-live file-naming convention documented and enforced - -- [x] All 7 managed launchers regenerated with template delegation arms - (2026-04-10) - -Remaining work (ordered by current priority in STATE.a2ml): - -- [ ] Golden-file regression tests pinning mint output for the 7 managed - launchers - - \* \[ \] macOS integration backend in \`launch-scaffolder-common - integration\` - -- [ ] SPARK integration hook for `integrity.rs` via Zig FFI - -- [ ] `standard` subcommand — still a scaffold stub - -- [ ] `platform` module — still a stub (runtime dispatch handled inside - the generated script today) - -- [ ] `integrity` module — still a stub (pending SHA-256 manifest - generator) - -- [ ] `exceptions` module — still a stub (per-app override merge) - -- [ ] Cross-platform CI (Linux/macOS/Windows matrix) - -- [ ] Migration of the 5 declared exceptions once the template grows - custom-mode hooks - -# Build - -```bash -# Standard cargo workflow -cargo build # debug build -cargo build --release # optimized, stripped, single-file binary -cargo test # run tests -cargo run -- --help # invoke the binary - -# Or via Justfile -just build -just test -just install # cargo install --path crates/launcher -``` - -# License - -This project is licensed under the Mozilla Public License, v. 2.0. See -the `LICENSE` file for details. - -SPDX-License-Identifier: CC-BY-SA-4.0 - -# Author - -Jonathan D.A. Jewell (hyperpolymath)\ -[j.d.a.jewell@open.ac](j.d.a.jewell@open.ac).uk - -# Relationship to other hyperpolymath projects - -Each consumer repo owns its own `.launcher.a2ml` config at its -repository root. `launch-scaffolder` `mint` writes the generated -`-launcher.sh` next to it. The pre-2026-04-10 pattern of pooling -launchers under `/var/mnt/eclipse/repos/.desktop-tools/` is deprecated; -`~/Desktop/Shortcuts/*.desktop` files have been repointed at the new -per-repo paths. - -**Scaffolder-managed consumers (as of 2026-04-10):** - -- **stapeln** — Visual Container Stack Designer (server-url, port 4010) - -- **burble** — WebRTC voice + control plane (server-url, port 4020) - -- **aerie** — network diagnostic suite (process) - -- **game-server-admin** — multi-game server orchestration (process) - -- **nqc** — NextGen Query Client, inside `nextgen-databases/` (process) - -- **panll** — panels framework (server-url, port 8000) - -- **project-wharf** — container/workload staging (process) - -**Declared exceptions** (still hand-written; see -`docs/launcher-exceptions-2026-04-10.md` for reasoning and migration -triggers): - -- `hypatia`, `invariant-path`, `opsm`, `ambientops`, `idaptik` - -The bulk-realignment goal: once `realign` is implemented, one command -will re-mint every managed launcher in the estate against the current -standard. diff --git a/SECURITY.adoc b/SECURITY.adoc new file mode 100644 index 0000000..3081ece --- /dev/null +++ b/SECURITY.adoc @@ -0,0 +1,16 @@ +== Security Policy + +=== Reporting a Vulnerability + +If you discover a security vulnerability, please report it responsibly: + +* *Email:* j.d.a.jewell@open.ac.uk +* *Do NOT* open a public issue for security vulnerabilities + +We will acknowledge receipt within 48 hours and provide a detailed +response within 7 days. + +=== Supported Versions + +Only the latest release on the `+main+` branch is supported with +security updates. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index a584804..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,18 +0,0 @@ - -# Security Policy - -## Reporting a Vulnerability - -If you discover a security vulnerability, please report it responsibly: - -- **Email:** j.d.a.jewell@open.ac.uk -- **Do NOT** open a public issue for security vulnerabilities - -We will acknowledge receipt within 48 hours and provide a detailed response within 7 days. - -## Supported Versions - -Only the latest release on the `main` branch is supported with security updates. diff --git a/docs/branch-protection-remediation-2026-04-10.adoc b/docs/branch-protection-remediation-2026-04-10.adoc new file mode 100644 index 0000000..f69b701 --- /dev/null +++ b/docs/branch-protection-remediation-2026-04-10.adoc @@ -0,0 +1,205 @@ +== Estate-wide branch-protection remediation — 2026-04-10 + +=== What triggered this + +While pushing the `+cmd_provision+` work to seven downstream launcher +repos (aerie, burble, game-server-admin, nextgen-databases, panll, +project-wharf, stapeln), some pushes printed a "`Bypassed rule +violations: Changes must be made through a pull request`" audit line and +others pushed silently. That inconsistency prompted an investigation: +the documented standard is "`PR required, 0 approvals, +enforce_admins=false, required_signatures ON`" — why were only some +repos enforcing it? + +=== What we found + +*The warnings are not the problem.* When a repo carries the full 5-rule +ruleset with admin listed as a bypass actor, GitHub emits the "`Bypassed +rule violations`" line as the audit trail for an _authorised_ admin +direct-push. That is the system working as designed — it is the +accountability record, not noise. + +*The real problem is drift.* A read-only audit against all 315 +non-archived `+hyperpolymath/*+` repos (see +`+docs/ruleset-audit-2026-04-10/+` for the raw data) surfaced this +distribution: + +[width="100%",cols="34%,33%,33%",options="header",] +|=== +|Class |Count |State +|OK |113 |Exactly the documented 5-rule reference — no action + +|DRIFT |168 |Ruleset exists but differs from reference (see breakdown +below) + +|MISSING |29 |No ruleset at all on the default branch — includes +`+launch-scaffolder+` itself + +|ERROR (API 403) |5 |Private repos on GitHub Free; rulesets require +Pro-or-higher on private repos +|=== + +*Drift shapes (non-exclusive — some repos match more than one):* + +* 147 have `+code_quality+` + 3 history rules + `+required_signatures+` +but are *missing `+pull_request+`*. +* 11 have 4 history rules + `+required_signatures+`, no +`+code_quality+`, still missing `+pull_request+`. +* 3 match the reference shape plus an extra `+required_status_checks+` +rule (legitimate CI contract; preserved). +* 4 are kitchen-sink rulesets with `+copilot_code_review+`, +`+code_scanning+`, `+required_deployments+` — looks like someone clicked +GitHub’s "`suggested ruleset`" button in the UI. +* 2 are minimal and missing both `+pull_request+` and +`+required_signatures+`. + +*The 5 ERROR cases* are all private repos: `+007+`, +`+.git-private-farm+`, `+hyperpolymath-sovereign-registry+`, +`+blog-drafts+`, `+repos-monorepo+`. The GitHub API returns +`+"Upgrade to GitHub Pro or make this repository public to enable this feature"+` +— rulesets on private repos need at least GitHub Pro. + +=== Reference shape + +Documented in +`+~/.claude/projects/-var-mnt-eclipse-repos/memory/feedback_branch_protection.md+` +and confirmed empirically against `+game-server-admin+` / `+panll+` / +`+project-wharf+`: + +[source,json] +---- +{ + "name": "Base", + "target": "branch", + "enforcement": "active", + "conditions": { "ref_name": { "exclude": [], "include": ["~DEFAULT_BRANCH"] } }, + "rules": [ + { "type": "required_signatures" }, + { "type": "deletion" }, + { "type": "non_fast_forward" }, + { "type": "required_linear_history" }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 0, + "dismiss_stale_reviews_on_push": false, + "required_reviewers": [], + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_review_thread_resolution": false, + "allowed_merge_methods": ["merge", "squash", "rebase"] + } + } + ], + "bypass_actors": [ + { "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "always" } + ] +} +---- + +`+actor_id=5+` is GitHub’s built-in Admin role. Repos with an additional +`+required_status_checks+` rule keep it — that’s an additive CI +contract, not drift. + +=== Remediation strategy + +Three waves, each gated on a human-readable diff before apply: + +[arabic] +. *Wave 1 — 29 MISSING repos → create the reference from scratch.* +Lowest risk (no existing state to clobber). +. *Wave 2 — 168 DRIFT repos → delete-and-recreate to match reference.* +Preserves `+required_status_checks+` where present. +. *Wave 3 — 5 private-Free repos → apply classic branch protection +(works on Free) OR switch to rulesets once GitHub Education / Pro is +available.* + +The three waves are independent and can be tackled in any order. + +=== Wave 1 outcome (2026-04-10) + +*21 repos brought to compliance; 8 upstream forks intentionally +skipped.* + +Forks skipped per explicit user decision: `+awesome-elixir+`, +`+awesome-haskell+`, `+awesome-lua+`, `+awesome-selfhosted+`, +`+awesome-v+`, `+file+`, `+lua-filters+`, `+rescript+`. Rationale: these +are not original hyperpolymath work, and a fork-local ruleset could +friction future rebases against upstream without delivering proportional +policy value. + +Apply sequence (single POST per repo via `+gh api+`): + +* 19/21 succeeded on the first POST. +* 2/21 (`+MinixSDK.jl+`, `+thejeffparadox+`) rejected with +`+"Validation Failed: Name must be unique"+`. Investigation showed both +had a pre-existing broken `+Base+` ruleset that the audit classified as +MISSING (correctly — neither was firing on the default branch): +** `+MinixSDK.jl+` had `+enforcement=disabled+`, `+include=["~ALL"]+`, 9 +rules including `+code_quality+`, `+code_scanning+`, +`+copilot_code_review+`, and 8 bypass actors (Write, Maintain, Admin, +plus 5 Integration apps). Effectively dead config. +** `+thejeffparadox+` had `+enforcement=active+` but `+include=[]+` +(targeted no branches), only 2 rules, no bypass actors. Effectively +orphaned. +* Both were delete-and-recreated cleanly. + +Post-apply audit: *21/21 compliant*. `+launch-scaffolder+` is now gated; +subsequent direct pushes from admin will emit the expected "`Bypassed +rule violations`" audit breadcrumb as the accountability record, +matching the rest of the estate. + +=== Wave 2 and 3 — deferred + +Wave 2 (168 DRIFT) and Wave 3 (5 private) are captured in +`+.machine_readable/6a2/STATE.a2ml+` as future work, and Wave 3 is +specifically waiting on GitHub Education approval. If Pro-via-Education +unlocks rulesets on private repos (the API error message says it does; +the rulesets docs are ambiguous and describe org-scoped rulesets), the +fastest empirical test after approval is: + +[source,bash] +---- +gh api --method PUT "repos/hyperpolymath/007/rulesets" \ + --input docs/ruleset-audit-2026-04-10/reference-ruleset.json +---- + +If that succeeds → the same tooling handles Wave 3 identically to Wave +1. If it fails → fall back to classic branch protection, which works on +Free for private repos and has equivalent semantics (PR required, linear +history, required signatures, no force push, no deletion, admin bypass +via `+enforce_admins=false+`). + +=== Takeaways + +[arabic] +. The "`Bypassed rule violations`" warnings are the correct audit trail +for admin direct-push through a PR gate. *Do not try to silence them.* +Their presence is proof the gate exists. +. The estate’s ruleset config had drifted substantially. 113/315 (36%) +conformed to the documented standard; the rest needed work. +. `+code_quality+` in a ruleset is a trap on a polyglot estate — CodeQL +doesn’t support every language we use, and the rule behaves +inconsistently. Gate code quality at the workflow layer, not the +branch-rule layer. +. GitHub Free blocks rulesets on private repos. This matters for any +estate with private-primary repos; plan for GitHub Pro/Education/Team +accordingly. +. A read-only estate audit before _any_ write is cheap and catches +classification bugs (e.g. the two "`MISSING`" repos that actually had +broken pre-existing rulesets). Always audit twice: once to classify, +once after apply to verify. + +=== Audit artefacts + +All raw data and scripts used for Wave 1 are archived in +`+docs/ruleset-audit-2026-04-10/+`: + +* `+repos.tsv+` — 315 non-archived repos with their privacy flag +* `+forks.txt+` — fork list used to exclude the 8 upstream forks +* `+report.jsonl+` — per-repo classification (OK/DRIFT/MISSING/ERROR) +* `+audit.sh+` — the read-only classifier (can be rerun at any time) +* `+reference-ruleset.json+` — the canonical body used for POSTs +* `+wave1-repos.txt+`, `+wave1-apply.txt+` — input lists +* `+wave1-plan.jsonl+` — the dry-run plan approved before apply +* `+wave1-results.tsv+` — per-repo outcome, including the two recoveries diff --git a/docs/branch-protection-remediation-2026-04-10.md b/docs/branch-protection-remediation-2026-04-10.md deleted file mode 100644 index 2895caa..0000000 --- a/docs/branch-protection-remediation-2026-04-10.md +++ /dev/null @@ -1,184 +0,0 @@ - -# Estate-wide branch-protection remediation — 2026-04-10 - -## What triggered this - -While pushing the `cmd_provision` work to seven downstream launcher -repos (aerie, burble, game-server-admin, nextgen-databases, panll, -project-wharf, stapeln), some pushes printed a "Bypassed rule -violations: Changes must be made through a pull request" audit line -and others pushed silently. That inconsistency prompted an -investigation: the documented standard is "PR required, 0 approvals, -enforce_admins=false, required_signatures ON" — why were only some -repos enforcing it? - -## What we found - -**The warnings are not the problem.** When a repo carries the full -5-rule ruleset with admin listed as a bypass actor, GitHub emits the -"Bypassed rule violations" line as the audit trail for an -*authorised* admin direct-push. That is the system working as -designed — it is the accountability record, not noise. - -**The real problem is drift.** A read-only audit against all 315 -non-archived `hyperpolymath/*` repos (see `docs/ruleset-audit-2026-04-10/` -for the raw data) surfaced this distribution: - -| Class | Count | State | -|---|---|---| -| OK | 113 | Exactly the documented 5-rule reference — no action | -| DRIFT | 168 | Ruleset exists but differs from reference (see breakdown below) | -| MISSING | 29 | No ruleset at all on the default branch — includes `launch-scaffolder` itself | -| ERROR (API 403) | 5 | Private repos on GitHub Free; rulesets require Pro-or-higher on private repos | - -**Drift shapes (non-exclusive — some repos match more than one):** - -- 147 have `code_quality` + 3 history rules + `required_signatures` but are **missing `pull_request`**. -- 11 have 4 history rules + `required_signatures`, no `code_quality`, still missing `pull_request`. -- 3 match the reference shape plus an extra `required_status_checks` rule (legitimate CI contract; preserved). -- 4 are kitchen-sink rulesets with `copilot_code_review`, `code_scanning`, `required_deployments` — looks like someone clicked GitHub's "suggested ruleset" button in the UI. -- 2 are minimal and missing both `pull_request` and `required_signatures`. - -**The 5 ERROR cases** are all private repos: `007`, -`.git-private-farm`, `hyperpolymath-sovereign-registry`, -`blog-drafts`, `repos-monorepo`. The GitHub API returns -`"Upgrade to GitHub Pro or make this repository public to enable this -feature"` — rulesets on private repos need at least GitHub Pro. - -## Reference shape - -Documented in -`~/.claude/projects/-var-mnt-eclipse-repos/memory/feedback_branch_protection.md` -and confirmed empirically against `game-server-admin` / `panll` / -`project-wharf`: - -```json -{ - "name": "Base", - "target": "branch", - "enforcement": "active", - "conditions": { "ref_name": { "exclude": [], "include": ["~DEFAULT_BRANCH"] } }, - "rules": [ - { "type": "required_signatures" }, - { "type": "deletion" }, - { "type": "non_fast_forward" }, - { "type": "required_linear_history" }, - { - "type": "pull_request", - "parameters": { - "required_approving_review_count": 0, - "dismiss_stale_reviews_on_push": false, - "required_reviewers": [], - "require_code_owner_review": false, - "require_last_push_approval": false, - "required_review_thread_resolution": false, - "allowed_merge_methods": ["merge", "squash", "rebase"] - } - } - ], - "bypass_actors": [ - { "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "always" } - ] -} -``` - -`actor_id=5` is GitHub's built-in Admin role. Repos with an additional -`required_status_checks` rule keep it — that's an additive CI contract, -not drift. - -## Remediation strategy - -Three waves, each gated on a human-readable diff before apply: - -1. **Wave 1 — 29 MISSING repos → create the reference from scratch.** Lowest risk (no existing state to clobber). -2. **Wave 2 — 168 DRIFT repos → delete-and-recreate to match reference.** Preserves `required_status_checks` where present. -3. **Wave 3 — 5 private-Free repos → apply classic branch protection (works on Free) OR switch to rulesets once GitHub Education / Pro is available.** - -The three waves are independent and can be tackled in any order. - -## Wave 1 outcome (2026-04-10) - -**21 repos brought to compliance; 8 upstream forks intentionally skipped.** - -Forks skipped per explicit user decision: `awesome-elixir`, -`awesome-haskell`, `awesome-lua`, `awesome-selfhosted`, `awesome-v`, -`file`, `lua-filters`, `rescript`. Rationale: these are not original -hyperpolymath work, and a fork-local ruleset could friction future -rebases against upstream without delivering proportional policy value. - -Apply sequence (single POST per repo via `gh api`): - -- 19/21 succeeded on the first POST. -- 2/21 (`MinixSDK.jl`, `thejeffparadox`) rejected with - `"Validation Failed: Name must be unique"`. Investigation showed both - had a pre-existing broken `Base` ruleset that the audit classified - as MISSING (correctly — neither was firing on the default branch): - - `MinixSDK.jl` had `enforcement=disabled`, `include=["~ALL"]`, 9 rules - including `code_quality`, `code_scanning`, `copilot_code_review`, - and 8 bypass actors (Write, Maintain, Admin, plus 5 Integration apps). - Effectively dead config. - - `thejeffparadox` had `enforcement=active` but `include=[]` - (targeted no branches), only 2 rules, no bypass actors. - Effectively orphaned. -- Both were delete-and-recreated cleanly. - -Post-apply audit: **21/21 compliant**. `launch-scaffolder` is now -gated; subsequent direct pushes from admin will emit the expected -"Bypassed rule violations" audit breadcrumb as the accountability -record, matching the rest of the estate. - -## Wave 2 and 3 — deferred - -Wave 2 (168 DRIFT) and Wave 3 (5 private) are captured in -`.machine_readable/6a2/STATE.a2ml` as future work, and Wave 3 is -specifically waiting on GitHub Education approval. If Pro-via-Education -unlocks rulesets on private repos (the API error message says it does; -the rulesets docs are ambiguous and describe org-scoped rulesets), the -fastest empirical test after approval is: - -```bash -gh api --method PUT "repos/hyperpolymath/007/rulesets" \ - --input docs/ruleset-audit-2026-04-10/reference-ruleset.json -``` - -If that succeeds → the same tooling handles Wave 3 identically to -Wave 1. If it fails → fall back to classic branch protection, which -works on Free for private repos and has equivalent semantics (PR -required, linear history, required signatures, no force push, no -deletion, admin bypass via `enforce_admins=false`). - -## Takeaways - -1. The "Bypassed rule violations" warnings are the correct audit trail - for admin direct-push through a PR gate. **Do not try to silence - them.** Their presence is proof the gate exists. -2. The estate's ruleset config had drifted substantially. 113/315 - (36%) conformed to the documented standard; the rest needed work. -3. `code_quality` in a ruleset is a trap on a polyglot estate — - CodeQL doesn't support every language we use, and the rule - behaves inconsistently. Gate code quality at the workflow layer, - not the branch-rule layer. -4. GitHub Free blocks rulesets on private repos. This matters for - any estate with private-primary repos; plan for GitHub - Pro/Education/Team accordingly. -5. A read-only estate audit before *any* write is cheap and catches - classification bugs (e.g. the two "MISSING" repos that actually - had broken pre-existing rulesets). Always audit twice: once to - classify, once after apply to verify. - -## Audit artefacts - -All raw data and scripts used for Wave 1 are archived in -`docs/ruleset-audit-2026-04-10/`: - -- `repos.tsv` — 315 non-archived repos with their privacy flag -- `forks.txt` — fork list used to exclude the 8 upstream forks -- `report.jsonl` — per-repo classification (OK/DRIFT/MISSING/ERROR) -- `audit.sh` — the read-only classifier (can be rerun at any time) -- `reference-ruleset.json` — the canonical body used for POSTs -- `wave1-repos.txt`, `wave1-apply.txt` — input lists -- `wave1-plan.jsonl` — the dry-run plan approved before apply -- `wave1-results.tsv` — per-repo outcome, including the two recoveries diff --git a/docs/compliance-audit-2026-04-10.adoc b/docs/compliance-audit-2026-04-10.adoc new file mode 100644 index 0000000..e6c1870 --- /dev/null +++ b/docs/compliance-audit-2026-04-10.adoc @@ -0,0 +1,347 @@ +== Launcher Compliance Audit — 2026-04-10 + +____ +*FROZEN SNAPSHOT.* This document records the pre-migration state of the +11 hand-written launchers in `+.desktop-tools/+` as of 2026-04-10, +_before_ any `+launch-scaffolder+` work. Do not update it to reflect +current state — the findings here are the historical justification for +subsequent scaffolder design decisions, and later documents (see below) +cross-reference this one by its frozen claims. + +*For current state of launcher management, read instead:* + +* `+README.adoc+` — live subcommand surface and architecture +* `+.machine_readable/6a2/STATE.a2ml+` — current milestone + completion +percentage +* `+docs/launcher-exceptions-2026-04-10.md+` — reconciliation against +this audit, including one correction (opsm runtime-shape classification; +see its "`Discrepancy 1`" section) +* `+docs/branch-protection-remediation-2026-04-10.md+` — estate-wide +ruleset remediation that followed the scaffolder work + +Of the 11 launchers audited here, 6 have since been migrated to +scaffolder management (aerie, burble, game-server-admin, nqc, panll, +project-wharf — plus stapeln, which the audit did not cover because +stapeln’s launcher lived under `+stapeln/scripts/+` not +`+.desktop-tools/+`). The remaining 5 are the declared exceptions +(hypatia, invariant-path, opsm, ambientops, idaptik) documented with +migration triggers in `+docs/launcher-exceptions-2026-04-10.md+`. +____ + +Read-only audit of the 11 hand-written launchers in +`+/var/mnt/eclipse/repos/.desktop-tools/*-launcher.sh+` against: + +* `+standards/docs/UX-standards/launcher-standard.adoc+` +* `+standards/docs/UX-standards/LM-LA-LIFECYCLE-STANDARD.adoc+` + +No launcher was modified. This document is the only file written inside +`+launch-scaffolder/+` for this audit. + +=== Scope + +[cols=",,",options="header",] +|=== +|# |Launcher |LOC +|1 |`+aerie-launcher.sh+` |104 +|2 |`+ambientops-launcher.sh+` |124 +|3 |`+burble-launcher.sh+` |152 +|4 |`+game-server-admin-launcher.sh+` |104 +|5 |`+hypatia-launcher.sh+` |73 +|6 |`+idaptik-launcher.sh+` |190 +|7 |`+invariant-path-launcher.sh+` |173 +|8 |`+nqc-launcher.sh+` |102 +|9 |`+opsm-launcher.sh+` |116 +|10 |`+panll-launcher.sh+` |161 +|11 |`+project-wharf-launcher.sh+` |104 +|=== + +=== Standard required modes + +Per `+launcher-standard.adoc §Standard Modes+`: + +`+--start+`, `+--stop+`, `+--status+`, `+--auto+` (default), +`+--browser+` (alias of `+--auto+`), `+--integ+`, `+--disinteg+`, +`+--help+` / `+-h+`. + +=== Per-launcher compliance matrix + +Legend: ✅ present · ⚠ implicit (falls through `+*+` to another mode) · +❌ missing · — N/A for runtime shape + +[width="100%",cols="10%,9%,9%,9%,9%,9%,9%,9%,9%,9%,9%",options="header",] +|=== +|Launcher |start |stop |status |auto |browser |integ |disinteg |help +|Extra modes |Runtime shape +|aerie |✅ |✅ |✅ |✅ |❌ |❌ |❌ |⚠ |— |background process (wraps +`+repo-quicklaunch.sh → just tour+`) + +|ambientops |✅ |✅ |✅ |✅ |❌ |❌ |❌ |⚠ |`+--toggle-input-devices+`, +`+--emergency-input-restore+` |background process + +|burble |✅ |✅ |✅ |✅ |⚠ (via `+*+`) |❌ |❌ |⚠ |— |server-with-URL +(`+http://localhost:4020+`, Phoenix) + +|game-server-admin |✅ |✅ |✅ |✅ |❌ |❌ |❌ |⚠ |`+--gossamer+` (alias +of `+--start+`) |background process (re-invokes +`+~/.local/bin/game-server-admin-launcher --gossamer+`) + +|hypatia |❌ |❌ |✅ |✅ |❌ |❌ |❌ |✅ |`+--tour+`, `+--gui+`, +`+--local+`, `+--dev+`, `+--cli+`, `+--scan+` |remote web app +(`+https://nesy-prover.dev+`), plus `+exec+`-based CLI subcommand + +|idaptik |✅ |✅ |✅ |✅ |✅ |❌ |❌ |⚠ |`+--web+`, `+--gossamer+`, +`+--tray+` |server-with-URL (`+http://localhost:8080+`, Deno) + +|invariant-path |— |— |✅ |✅ |— |❌ |❌ |✅ |`+--scan+`, +`+--scan-file+`, `+--cli+`, `+--open-output+` |bespoke one-shot CLI +(scan-on-demand, no daemon) + +|nqc |✅ |✅ |✅ |✅ |❌ |❌ |❌ |⚠ |— |background process (GUI via +`+~/.bin/nqc --gui+`) + +|opsm |✅ |✅ |✅ |✅ |❌ |❌ |❌ |⚠ |`+--system-update+` |bespoke +(`+bash -lc+` wrapper around `+opsm-runtime+` / profile-sourced +functions) + +|panll |✅ |✅ |✅ |✅ |✅ |❌ |❌ |⚠ |bare-word aliases: `+serve+`, +`+start+`, `+stop+`, `+status+`, `+browser+`, `+web+`, `+dev+` +|server-with-URL (`+http://localhost:8000/public/+`, `+just serve+`) + +|project-wharf |✅ |✅ |✅ |✅ |❌ |❌ |❌ |⚠ |— |background process +(wraps `+repo-quicklaunch.sh → just tour+`) +|=== + +=== Per-launcher security notes + +`+set -euo pipefail+`, variable quoting, `+eval+`, world-writable +`+/tmp+` PID files, and unpinned `+curl | bash+`. + +[width="99%",cols="16%,14%,14%,14%,14%,14%,14%",options="header",] +|=== +|Launcher |`+set -euo pipefail+` |Quoting |`+eval+`? |`+/tmp+` PID file +|`+curl \| bash+` |Other +|aerie |✅ |✅ |none |`+/tmp/aerie.pid+` (standard-compliant predictable +name, no `+mktemp+`) |none |Hardcoded +`+/var/mnt/eclipse/repos/.desktop-tools/launchers/repo-quicklaunch.sh+` +— portability, not security + +|ambientops |✅ |✅ |none |`+/tmp/ambientops.pid+` |none |Same hardcoded +`+repo-quicklaunch.sh+` path + +|burble |✅ |✅ |none |`+/tmp/burble-server.pid+` |none |`+curl+` used +only for local readiness probe against `+$URL+`; not piped to shell + +|game-server-admin |✅ |✅ |none |`+/tmp/game-server-admin.pid+` |none +|Hardcoded `+/home/hyper/.local/bin/game-server-admin-launcher+` +(double-indirection to another launcher) + +|hypatia |✅ |✅ |none |no PID file (exec-based, foreground) |none +|`+exec gossamer+` / `+xdg-open+` for remote URL; no daemon to track + +|idaptik |✅ |✅ |none |`+/tmp/idaptik-server.pid+` |none |Pre-start +`+lsof -i :8080+` port probe; `+curl+` only for readiness probe; +`+notify-send+` optional + +|invariant-path |✅ |✅ (all `+${…}+` form) |none |no PID file +(one-shot) |none |Clean + +|nqc |✅ |✅ |none |`+/tmp/nqc.pid+` |none |Hardcoded +`+/home/hyper/.bin/nqc+`; no `+REPO_DIR+` (no `+cd+`) + +|opsm |✅ |✅ (at top level) |none, *but* see note |`+/tmp/opsm.pid+` +|none |⚠ `+nohup bash -lc "$COMMAND_SCRIPT"+` and +`+nohup bash -c '…opsm system-update…'+` — composed-command-string +pattern. Not injection (no external input), but fragile: sources +`+~/.bashrc.d/tools/opsm+` from inside a nohup login shell, ends in +`+exec bash+`, so the tracked PID is a stand-in for an interactive +shell. Also breaks on systems without that profile fragment. + +|panll |✅ |✅ |none |`+/tmp/panll-server.pid+` |none |`+curl+` only for +readiness probe; exports `+BROWSERSLIST_IGNORE_OLD_DATA=1+` to silence +caniuse warnings + +|project-wharf |✅ |✅ |none |`+/tmp/project-wharf.pid+` |none |Same +hardcoded `+repo-quicklaunch.sh+` path as aerie/ambientops +|=== + +None of the 11 use `+eval+`. None pipe remote `+curl+` output to a +shell. All 11 set `+set -euo pipefail+`. Variable quoting is generally +clean; no unquoted `+$VAR+` expansions that the auditor could find. + +=== Fleet-level findings + +==== 1. `+--integ+` / `+--disinteg+` coverage: *0 / 11* + +No launcher implements system-integration or dis-integration modes. The +Desktop-file / Start-Menu / `+~/.local/bin/+` install surface described +in `+launcher-standard.adoc §System Integration Modes+` is not present +anywhere. This is the single largest gap against the standard and the +most impactful one to fix, since it is _the_ reason the two standards +(`+launcher-standard+` and `+LM-LA-LIFECYCLE-STANDARD+`) were unified: +one entry point for install, uninstall, and runtime. + +==== 2. Explicit `+--help+` / `+-h+`: *2 / 11* + +Only `+hypatia-launcher.sh+` and `+invariant-path-launcher.sh+` print a +usage text. The other nine rely on the `+--auto|*+` fall-through, so +`+./launcher.sh --help+` actually _starts_ the application. The standard +requires help to print usage text plus detected platform and the files +the launcher reads/writes; no launcher satisfies the full requirement. + +==== 3. Explicit `+--browser+` branch: *2 / 11* + +Only `+idaptik+` and `+panll+` have a dedicated `+--browser+` / +`+--web+` branch. `+burble+` accepts it implicitly via `+*+` +fall-through (which does open a browser, so behaviour is correct but the +case arm is not explicit). The remaining eight either do not launch a +browser at all or bury the behaviour inside `+--auto+`. + +==== 4. Readiness checking (`+wait_for_server+`): *3 / 11* + +Only the three "`server-with-URL`" launchers (`+burble+`, `+idaptik+`, +`+panll+`) implement `+wait_for_server+`. This is consistent with the +standard — the pattern is only required for web/server apps — but it +means the scaffolder should detect runtime shape and only emit +`+wait_for_server+` for the `+server-with-url+` shape. + +==== 5. Port-conflict pre-check: *1 / 11* + +Only `+idaptik+` checks `+lsof -i :PORT+` before starting. `+burble+` +and `+panll+` will silently run into port conflicts and surface them as +"`server did not start within N seconds`". Worth promoting to a standard +pattern for the server-with-url shape. + +==== 6. Hardcoded `+$HOME+` paths: *5 / 11* + +`+aerie+`, `+ambientops+`, `+project-wharf+` hardcode +`+/var/mnt/eclipse/repos/.desktop-tools/launchers/repo-quicklaunch.sh+`. +`+game-server-admin+` hardcodes +`+/home/hyper/.local/bin/game-server-admin-launcher+`. `+nqc+` hardcodes +`+/home/hyper/.bin/nqc+`. + +These are portability, not security, issues — but they directly +contradict design principle #5 ("`no elevated privileges, user-level +paths`") and principle #2 ("`cross-platform`"). The scaffolder should +emit `+${HOME}+` or resolve from `+$PATH+` via `+command -v+`. + +==== 7. Double-indirection in `+game-server-admin+` + +`+game-server-admin-launcher.sh+` in `+.desktop-tools/+` calls +`+/home/hyper/.local/bin/game-server-admin-launcher --gossamer+`. If +that second file is itself this launcher (copied there by a hypothetical +past `+--integ+`), this is a loop. If it is a different file, there are +two truths about what "`launching Game Server Admin`" means. Worth +untangling before the scaffolder emits this class of launcher. + +==== 8. OPSM composed-command pattern + +`+opsm-launcher.sh+` uses: + +[source,sh] +---- +nohup bash -lc "$COMMAND_SCRIPT" >"$LOG_FILE" 2>&1 & +---- + +where `+$COMMAND_SCRIPT+` is a literal shell snippet that sources +`+~/.bashrc.d/tools/opsm+`, prints diagnostic output, and ends in +`+exec bash+`. The tracked PID is therefore a stand-in for an +interactive shell. It is the only launcher in the fleet whose daemon is +actually a login shell. Not injectable (no external input), but: + +* fragile (requires the exact profile fragment to exist); +* non-portable (no `+~/.bashrc.d/+` on macOS); +* the PID file does not meaningfully represent the OPSM runtime. + +Recommend the scaffolder treat "`shell-function wrapper around a +profile-sourced tool`" as a distinct runtime shape that does _not_ +attempt daemon tracking, and instead runs the command synchronously in +the foreground (like `+hypatia-launcher.sh --cli+`). + +==== 9. `+hypatia-launcher.sh+` — a legitimately different shape + +`+hypatia-launcher.sh+` is the odd one out: it has no PID file, no +`+start_server+`, no `+stop_server+`. It `+exec+`s into either +`+gossamer+` (for the remote GUI) or `+hypatia-cli.sh+` (for local +scan), and its `+--status+` only reports "`launcher ready, URL: …`". +This is correct for its runtime shape — *remote web app with optional +local CLI subcommand* — and the scaffolder should recognise this shape +rather than force it into the daemon template. + +==== 10. `+invariant-path-launcher.sh+` — also legitimately different + +`+invariant-path-launcher.sh+` is the one-shot CLI shape: it runs a +scan, writes to `+/tmp/invariant-path-last-scan.json+`, and exits. +`+--start+` / `+--stop+` are semantically meaningless; the scaffolder +should emit only `+--status+` (last scan info) and `+--help+`, not the +daemon modes. It is also the only launcher that uses `+${VAR}+` brace +form consistently, has its own `+--help+` usage text, and redirects +stderr to a log file per invocation. + +=== Runtime-shape taxonomy observed + +The 11 launchers cluster into four distinct runtime shapes, suggesting +the scaffolder should accept a `+shape:+` field in its per-app manifest: + +[width="100%",cols="25%,25%,25%,25%",options="header",] +|=== +|Shape |Count |Launchers |Characteristic +|`+background-process+` |6 |aerie, ambientops, game-server-admin, nqc, +opsm, project-wharf |`+nohup+` + PID file, no URL, no readiness probe + +|`+server-with-url+` |3 |burble, idaptik, panll |`+nohup+` + PID file + +`+wait_for_server+` against `+$URL+` + `+open_browser+` + +|`+remote-web-app+` |1 |hypatia |no daemon; +`+exec gossamer+`/`+xdg-open+` against a remote URL; optional `+exec+` +CLI subcommand + +|`+one-shot-cli+` |1 |invariant-path |run-and-exit; outputs to a +well-known file; `+--status+` reports last run +|=== + +The standard’s reference template covers `+server-with-url+` well. The +other three shapes are under-specified and are where hand-written drift +has accumulated. + +=== Summary + +* *`+set -euo pipefail+`*: 11/11 ✅ +* *Variable quoting*: 11/11 ✅ +* *`+eval+`*: 0/11 ✅ +* *`+curl | bash+`*: 0/11 ✅ +* *`+--start/--stop/--status/--auto+`* (for applicable shapes): 9/9 ✅ +* *Explicit `+--browser+` branch*: 2/11 ⚠ +* *Explicit `+--help+`*: 2/11 ⚠ +* *`+--integ+`*: 0/11 ❌ +* *`+--disinteg+`*: 0/11 ❌ +* *Readiness check* (for `+server-with-url+` shape): 3/3 ✅ +* *Port pre-check* (for `+server-with-url+` shape): 1/3 ⚠ +* *Hardcoded `+$HOME+` paths*: 5/11 ⚠ +* *Runtime shapes observed*: 4 (scaffolder manifest should carry a shape +field) + +The fleet is *security-clean*, *structurally consistent within each +shape*, and *uniformly missing the `+--integ+` / `+--disinteg+` / +explicit `+--help+`* trio. Those three modes and the portability fixes +are the main work items for whichever session retires the hand-written +launchers via `+launch-scaffolder+`. + +=== Files consulted (read-only) + +* `+/var/mnt/eclipse/repos/developer-ecosystem/standards/docs/UX-standards/launcher-standard.adoc+` +* `+/var/mnt/eclipse/repos/developer-ecosystem/standards/docs/UX-standards/LM-LA-LIFECYCLE-STANDARD.adoc+` +* `+/var/mnt/eclipse/repos/.desktop-tools/aerie-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/ambientops-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/burble-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/game-server-admin-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/hypatia-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/idaptik-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/invariant-path-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/nqc-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/opsm-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/panll-launcher.sh+` +* `+/var/mnt/eclipse/repos/.desktop-tools/project-wharf-launcher.sh+` + +No file was modified. No file inside +`+/var/mnt/eclipse/repos/launch-scaffolder/+` other than this document +was written. diff --git a/docs/compliance-audit-2026-04-10.md b/docs/compliance-audit-2026-04-10.md deleted file mode 100644 index b262ab2..0000000 --- a/docs/compliance-audit-2026-04-10.md +++ /dev/null @@ -1,263 +0,0 @@ - -# Launcher Compliance Audit — 2026-04-10 - -> **FROZEN SNAPSHOT.** This document records the pre-migration state -> of the 11 hand-written launchers in `.desktop-tools/` as of -> 2026-04-10, *before* any `launch-scaffolder` work. Do not update it -> to reflect current state — the findings here are the historical -> justification for subsequent scaffolder design decisions, and later -> documents (see below) cross-reference this one by its frozen claims. -> -> **For current state of launcher management, read instead:** -> -> - `README.adoc` — live subcommand surface and architecture -> - `.machine_readable/6a2/STATE.a2ml` — current milestone + completion percentage -> - `docs/launcher-exceptions-2026-04-10.md` — reconciliation against this audit, including one correction (opsm runtime-shape classification; see its "Discrepancy 1" section) -> - `docs/branch-protection-remediation-2026-04-10.md` — estate-wide ruleset remediation that followed the scaffolder work -> -> Of the 11 launchers audited here, 6 have since been migrated to -> scaffolder management (aerie, burble, game-server-admin, nqc, panll, -> project-wharf — plus stapeln, which the audit did not cover because -> stapeln's launcher lived under `stapeln/scripts/` not -> `.desktop-tools/`). The remaining 5 are the declared exceptions -> (hypatia, invariant-path, opsm, ambientops, idaptik) documented -> with migration triggers in `docs/launcher-exceptions-2026-04-10.md`. - -Read-only audit of the 11 hand-written launchers in -`/var/mnt/eclipse/repos/.desktop-tools/*-launcher.sh` against: - -- `standards/docs/UX-standards/launcher-standard.adoc` -- `standards/docs/UX-standards/LM-LA-LIFECYCLE-STANDARD.adoc` - -No launcher was modified. This document is the only file written inside -`launch-scaffolder/` for this audit. - -## Scope - -| # | Launcher | LOC | -|---|---|---| -| 1 | `aerie-launcher.sh` | 104 | -| 2 | `ambientops-launcher.sh` | 124 | -| 3 | `burble-launcher.sh` | 152 | -| 4 | `game-server-admin-launcher.sh` | 104 | -| 5 | `hypatia-launcher.sh` | 73 | -| 6 | `idaptik-launcher.sh` | 190 | -| 7 | `invariant-path-launcher.sh` | 173 | -| 8 | `nqc-launcher.sh` | 102 | -| 9 | `opsm-launcher.sh` | 116 | -| 10 | `panll-launcher.sh` | 161 | -| 11 | `project-wharf-launcher.sh` | 104 | - -## Standard required modes - -Per `launcher-standard.adoc §Standard Modes`: - -`--start`, `--stop`, `--status`, `--auto` (default), `--browser` (alias of -`--auto`), `--integ`, `--disinteg`, `--help` / `-h`. - -## Per-launcher compliance matrix - -Legend: ✅ present · ⚠ implicit (falls through `*` to another mode) · ❌ missing · — N/A for runtime shape - -| Launcher | start | stop | status | auto | browser | integ | disinteg | help | Extra modes | Runtime shape | -|---|---|---|---|---|---|---|---|---|---|---| -| aerie | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ⚠ | — | background process (wraps `repo-quicklaunch.sh → just tour`) | -| ambientops | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ⚠ | `--toggle-input-devices`, `--emergency-input-restore` | background process | -| burble | ✅ | ✅ | ✅ | ✅ | ⚠ (via `*`) | ❌ | ❌ | ⚠ | — | server-with-URL (`http://localhost:4020`, Phoenix) | -| game-server-admin | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ⚠ | `--gossamer` (alias of `--start`) | background process (re-invokes `~/.local/bin/game-server-admin-launcher --gossamer`) | -| hypatia | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | `--tour`, `--gui`, `--local`, `--dev`, `--cli`, `--scan` | remote web app (`https://nesy-prover.dev`), plus `exec`-based CLI subcommand | -| idaptik | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ⚠ | `--web`, `--gossamer`, `--tray` | server-with-URL (`http://localhost:8080`, Deno) | -| invariant-path | — | — | ✅ | ✅ | — | ❌ | ❌ | ✅ | `--scan`, `--scan-file`, `--cli`, `--open-output` | bespoke one-shot CLI (scan-on-demand, no daemon) | -| nqc | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ⚠ | — | background process (GUI via `~/.bin/nqc --gui`) | -| opsm | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ⚠ | `--system-update` | bespoke (`bash -lc` wrapper around `opsm-runtime` / profile-sourced functions) | -| panll | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ⚠ | bare-word aliases: `serve`, `start`, `stop`, `status`, `browser`, `web`, `dev` | server-with-URL (`http://localhost:8000/public/`, `just serve`) | -| project-wharf | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ⚠ | — | background process (wraps `repo-quicklaunch.sh → just tour`) | - -## Per-launcher security notes - -`set -euo pipefail`, variable quoting, `eval`, world-writable `/tmp` PID files, -and unpinned `curl | bash`. - -| Launcher | `set -euo pipefail` | Quoting | `eval`? | `/tmp` PID file | `curl \| bash` | Other | -|---|---|---|---|---|---|---| -| aerie | ✅ | ✅ | none | `/tmp/aerie.pid` (standard-compliant predictable name, no `mktemp`) | none | Hardcoded `/var/mnt/eclipse/repos/.desktop-tools/launchers/repo-quicklaunch.sh` — portability, not security | -| ambientops | ✅ | ✅ | none | `/tmp/ambientops.pid` | none | Same hardcoded `repo-quicklaunch.sh` path | -| burble | ✅ | ✅ | none | `/tmp/burble-server.pid` | none | `curl` used only for local readiness probe against `$URL`; not piped to shell | -| game-server-admin | ✅ | ✅ | none | `/tmp/game-server-admin.pid` | none | Hardcoded `/home/hyper/.local/bin/game-server-admin-launcher` (double-indirection to another launcher) | -| hypatia | ✅ | ✅ | none | no PID file (exec-based, foreground) | none | `exec gossamer` / `xdg-open` for remote URL; no daemon to track | -| idaptik | ✅ | ✅ | none | `/tmp/idaptik-server.pid` | none | Pre-start `lsof -i :8080` port probe; `curl` only for readiness probe; `notify-send` optional | -| invariant-path | ✅ | ✅ (all `${…}` form) | none | no PID file (one-shot) | none | Clean | -| nqc | ✅ | ✅ | none | `/tmp/nqc.pid` | none | Hardcoded `/home/hyper/.bin/nqc`; no `REPO_DIR` (no `cd`) | -| opsm | ✅ | ✅ (at top level) | none, **but** see note | `/tmp/opsm.pid` | none | ⚠ `nohup bash -lc "$COMMAND_SCRIPT"` and `nohup bash -c '…opsm system-update…'` — composed-command-string pattern. Not injection (no external input), but fragile: sources `~/.bashrc.d/tools/opsm` from inside a nohup login shell, ends in `exec bash`, so the tracked PID is a stand-in for an interactive shell. Also breaks on systems without that profile fragment. | -| panll | ✅ | ✅ | none | `/tmp/panll-server.pid` | none | `curl` only for readiness probe; exports `BROWSERSLIST_IGNORE_OLD_DATA=1` to silence caniuse warnings | -| project-wharf | ✅ | ✅ | none | `/tmp/project-wharf.pid` | none | Same hardcoded `repo-quicklaunch.sh` path as aerie/ambientops | - -None of the 11 use `eval`. None pipe remote `curl` output to a shell. All 11 -set `set -euo pipefail`. Variable quoting is generally clean; no unquoted -`$VAR` expansions that the auditor could find. - -## Fleet-level findings - -### 1. `--integ` / `--disinteg` coverage: **0 / 11** - -No launcher implements system-integration or dis-integration modes. The -Desktop-file / Start-Menu / `~/.local/bin/` install surface described in -`launcher-standard.adoc §System Integration Modes` is not present anywhere. -This is the single largest gap against the standard and the most impactful -one to fix, since it is *the* reason the two standards (`launcher-standard` -and `LM-LA-LIFECYCLE-STANDARD`) were unified: one entry point for install, -uninstall, and runtime. - -### 2. Explicit `--help` / `-h`: **2 / 11** - -Only `hypatia-launcher.sh` and `invariant-path-launcher.sh` print a usage -text. The other nine rely on the `--auto|*` fall-through, so `./launcher.sh ---help` actually *starts* the application. The standard requires help to -print usage text plus detected platform and the files the launcher -reads/writes; no launcher satisfies the full requirement. - -### 3. Explicit `--browser` branch: **2 / 11** - -Only `idaptik` and `panll` have a dedicated `--browser` / `--web` branch. -`burble` accepts it implicitly via `*` fall-through (which does open a -browser, so behaviour is correct but the case arm is not explicit). The -remaining eight either do not launch a browser at all or bury the behaviour -inside `--auto`. - -### 4. Readiness checking (`wait_for_server`): **3 / 11** - -Only the three "server-with-URL" launchers (`burble`, `idaptik`, `panll`) -implement `wait_for_server`. This is consistent with the standard — the -pattern is only required for web/server apps — but it means the scaffolder -should detect runtime shape and only emit `wait_for_server` for the -`server-with-url` shape. - -### 5. Port-conflict pre-check: **1 / 11** - -Only `idaptik` checks `lsof -i :PORT` before starting. `burble` and `panll` -will silently run into port conflicts and surface them as "server did not -start within N seconds". Worth promoting to a standard pattern for the -server-with-url shape. - -### 6. Hardcoded `$HOME` paths: **5 / 11** - -`aerie`, `ambientops`, `project-wharf` hardcode -`/var/mnt/eclipse/repos/.desktop-tools/launchers/repo-quicklaunch.sh`. -`game-server-admin` hardcodes `/home/hyper/.local/bin/game-server-admin-launcher`. -`nqc` hardcodes `/home/hyper/.bin/nqc`. - -These are portability, not security, issues — but they directly contradict -design principle #5 ("no elevated privileges, user-level paths") and -principle #2 ("cross-platform"). The scaffolder should emit `${HOME}` or -resolve from `$PATH` via `command -v`. - -### 7. Double-indirection in `game-server-admin` - -`game-server-admin-launcher.sh` in `.desktop-tools/` calls -`/home/hyper/.local/bin/game-server-admin-launcher --gossamer`. If that -second file is itself this launcher (copied there by a hypothetical past -`--integ`), this is a loop. If it is a different file, there are two truths -about what "launching Game Server Admin" means. Worth untangling before -the scaffolder emits this class of launcher. - -### 8. OPSM composed-command pattern - -`opsm-launcher.sh` uses: - -```sh -nohup bash -lc "$COMMAND_SCRIPT" >"$LOG_FILE" 2>&1 & -``` - -where `$COMMAND_SCRIPT` is a literal shell snippet that sources -`~/.bashrc.d/tools/opsm`, prints diagnostic output, and ends in `exec bash`. -The tracked PID is therefore a stand-in for an interactive shell. It is the -only launcher in the fleet whose daemon is actually a login shell. Not -injectable (no external input), but: - -- fragile (requires the exact profile fragment to exist); -- non-portable (no `~/.bashrc.d/` on macOS); -- the PID file does not meaningfully represent the OPSM runtime. - -Recommend the scaffolder treat "shell-function wrapper around a -profile-sourced tool" as a distinct runtime shape that does *not* attempt -daemon tracking, and instead runs the command synchronously in the -foreground (like `hypatia-launcher.sh --cli`). - -### 9. `hypatia-launcher.sh` — a legitimately different shape - -`hypatia-launcher.sh` is the odd one out: it has no PID file, no -`start_server`, no `stop_server`. It `exec`s into either `gossamer` (for -the remote GUI) or `hypatia-cli.sh` (for local scan), and its `--status` -only reports "launcher ready, URL: …". This is correct for its runtime -shape — **remote web app with optional local CLI subcommand** — and the -scaffolder should recognise this shape rather than force it into the -daemon template. - -### 10. `invariant-path-launcher.sh` — also legitimately different - -`invariant-path-launcher.sh` is the one-shot CLI shape: it runs a scan, -writes to `/tmp/invariant-path-last-scan.json`, and exits. `--start` / -`--stop` are semantically meaningless; the scaffolder should emit only -`--status` (last scan info) and `--help`, not the daemon modes. It is also -the only launcher that uses `${VAR}` brace form consistently, has its own -`--help` usage text, and redirects stderr to a log file per invocation. - -## Runtime-shape taxonomy observed - -The 11 launchers cluster into four distinct runtime shapes, suggesting the -scaffolder should accept a `shape:` field in its per-app manifest: - -| Shape | Count | Launchers | Characteristic | -|---|---|---|---| -| `background-process` | 6 | aerie, ambientops, game-server-admin, nqc, opsm, project-wharf | `nohup` + PID file, no URL, no readiness probe | -| `server-with-url` | 3 | burble, idaptik, panll | `nohup` + PID file + `wait_for_server` against `$URL` + `open_browser` | -| `remote-web-app` | 1 | hypatia | no daemon; `exec gossamer`/`xdg-open` against a remote URL; optional `exec` CLI subcommand | -| `one-shot-cli` | 1 | invariant-path | run-and-exit; outputs to a well-known file; `--status` reports last run | - -The standard's reference template covers `server-with-url` well. The other -three shapes are under-specified and are where hand-written drift has -accumulated. - -## Summary - -- **`set -euo pipefail`**: 11/11 ✅ -- **Variable quoting**: 11/11 ✅ -- **`eval`**: 0/11 ✅ -- **`curl | bash`**: 0/11 ✅ -- **`--start/--stop/--status/--auto`** (for applicable shapes): 9/9 ✅ -- **Explicit `--browser` branch**: 2/11 ⚠ -- **Explicit `--help`**: 2/11 ⚠ -- **`--integ`**: 0/11 ❌ -- **`--disinteg`**: 0/11 ❌ -- **Readiness check** (for `server-with-url` shape): 3/3 ✅ -- **Port pre-check** (for `server-with-url` shape): 1/3 ⚠ -- **Hardcoded `$HOME` paths**: 5/11 ⚠ -- **Runtime shapes observed**: 4 (scaffolder manifest should carry a shape field) - -The fleet is **security-clean**, **structurally consistent within each -shape**, and **uniformly missing the `--integ` / `--disinteg` / explicit -`--help`** trio. Those three modes and the portability fixes are the main -work items for whichever session retires the hand-written launchers via -`launch-scaffolder`. - -## Files consulted (read-only) - -- `/var/mnt/eclipse/repos/developer-ecosystem/standards/docs/UX-standards/launcher-standard.adoc` -- `/var/mnt/eclipse/repos/developer-ecosystem/standards/docs/UX-standards/LM-LA-LIFECYCLE-STANDARD.adoc` -- `/var/mnt/eclipse/repos/.desktop-tools/aerie-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/ambientops-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/burble-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/game-server-admin-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/hypatia-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/idaptik-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/invariant-path-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/nqc-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/opsm-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/panll-launcher.sh` -- `/var/mnt/eclipse/repos/.desktop-tools/project-wharf-launcher.sh` - -No file was modified. No file inside `/var/mnt/eclipse/repos/launch-scaffolder/` -other than this document was written. diff --git a/docs/launcher-exceptions-2026-04-10.adoc b/docs/launcher-exceptions-2026-04-10.adoc new file mode 100644 index 0000000..d446e30 --- /dev/null +++ b/docs/launcher-exceptions-2026-04-10.adoc @@ -0,0 +1,260 @@ +== Launcher Scaffolder Exceptions — 2026-04-10 + +Five launchers in `+/var/mnt/eclipse/repos/.desktop-tools/+` are +declared *out of scope for `+launch-scaffolder+` minting* as of +2026-04-10. They stay hand-written for concrete reasons captured below. +Each row records what would have to change in `+launch-scaffolder+` +before the launcher could be moved into scaffolder management. + +Companion document: +link:compliance-audit-2026-04-10.md[`+compliance-audit-2026-04-10.md+`]. +See link:#cross-reference-against-the-compliance-audit[§Cross-reference +against the compliance audit] at the end for the reconciliation notes — +discrepancies are flagged, not silently patched. + +=== Exceptions table + +[width="100%",cols="20%,20%,20%,20%,20%",options="header",] +|=== +|Launcher |Current location |Reason it can’t be scaffolder-managed +|Extra / custom modes exposed beyond the standard template |Migration +trigger (what would have to land in `+launch-scaffolder+` first) +|`+hypatia-launcher.sh+` |`+/var/mnt/eclipse/repos/.desktop-tools/+` +|Remote web app (`+https://nesy-prover.dev+`) with a *conditional* +front-end: if `+gossamer+` is on `+PATH+` _and_ +`+~/.config/hypatia/gossamer.conf.json+` exists, `+exec gossamer+`; +otherwise fall through to `+xdg-open+`/`+firefox+`. No daemon, no PID +file, no `+start+`/`+stop+` semantics. The `+--cli+` mode `+exec+`s a +separate script (`+hypatia-cli.sh+`) and is a different runtime shape +again. |`+--tour+`, `+--gui+`, `+--local+`, `+--dev+`, `+--cli+`, +`+--scan+`, plus `+exec gossamer …+` dispatch |`+launch-scaffolder+` +needs a `+shape: remote-web-app+` runtime in its template engine that +(a) emits no daemon machinery, (b) supports a conditional +gossamer-vs-browser dispatch block, and (c) allows a sibling +`+exec+`-delegated CLI subcommand (`+--cli+`/`+--scan+`) to live in the +same launcher. + +|`+invariant-path-launcher.sh+` +|`+/var/mnt/eclipse/repos/.desktop-tools/+` |Bespoke Rust-CLI wrapper: +every invocation shells out to +`+cargo run --manifest-path … -p invariant-path-cli --+`, writes results +to `+/tmp/invariant-path-last-scan.json+`, and exits. Not a server, not +a daemon, no readiness probe, no browser. `+--status+` reports the last +scan output, not a running process. Also owns its own `+--scan-file+` +and `+--open-output+` modes that touch a well-known output file. +|`+--scan [repo] [profile]+`, `+--scan-file [profile]+`, +`+--cli +` (pass-through), `+--open-output+`, `+--status+` +(reports last-scan summary), `+--auto+` (scan default repo) — all +dispatched through `+cargo run+` |`+launch-scaffolder+` needs a +`+shape: one-shot-cli+` runtime that emits (a) no PID file, (b) a +`+cargo run+` / `+just+` / `+deno task+` dispatcher with configurable +backend, (c) a well-known `+OUTPUT_FILE+` contract, (d) an optional +`+jq+`-based summariser, and (e) a `+--status+` that reports "`last run +output`" instead of "`process state`". + +|`+opsm-launcher.sh+` |`+/var/mnt/eclipse/repos/.desktop-tools/+` |Its +runtime is *a login shell*. The "`daemon`" is +`+nohup bash -lc "$COMMAND_SCRIPT"+` where `+$COMMAND_SCRIPT+` sources +`+~/.bashrc.d/tools/opsm+`, runs +`+opsm-runtime list && opsm-runtime doctor+`, and ends in `+exec bash+`. +The tracked PID is therefore an interactive bash session, not the real +OPSM runtime. Also depends on a profile fragment that only exists on the +author’s machine, so the launcher is non-portable by construction. +|`+--system-update+` (custom), plus the implicit "`source profile → +`+exec bash+``" behaviour of `+--start+` / `+--auto+` +|`+launch-scaffolder+` needs a `+shape: shell-context+` runtime that (a) +deliberately does *not* produce a PID-file-tracked daemon, (b) runs the +command synchronously in a foreground login shell, (c) declares its +profile-source dependencies in the per-app manifest so a +dependency-check step can detect missing fragments, and (d) supports +arbitrary extra modes like `+--system-update+` as first-class entries in +the manifest. Alternative: the user decides OPSM should not have a +launcher at all and is invoked interactively only, in which case this +file is deleted rather than migrated. + +|`+ambientops-launcher.sh+` |`+/var/mnt/eclipse/repos/.desktop-tools/+` +|Not just the `+.sh+` — its companion `+.desktop+` file exposes +input-device management as `+Desktop Action+` entries, so the launcher +contract is *file-pair* (`+.sh+` + `+.desktop+`), not just the shell +script. The two custom `+.sh+` modes (`+--toggle-input-devices+`, +`+--emergency-input-restore+`) are there specifically to be reachable +from the Action entries in the `+.desktop+` file. +|`+--toggle-input-devices+`, `+--emergency-input-restore+`, both called +via `+repo-quicklaunch.sh … just +` |`+launch-scaffolder+` needs +first-class support for *extra `+.desktop+` `+[Desktop Action …]+` +entries* declared in the per-app manifest, and the ability to bind each +Action to a custom mode name in the emitted `+.sh+`. Today the +scaffolder only emits the stock `+stop;status;+` Action pair described +in `+launcher-standard.adoc §Desktop File Standard+`; it cannot emit +arbitrary additional Actions. + +|`+idaptik-launcher.sh+` |`+/var/mnt/eclipse/repos/.desktop-tools/+` +|Similar to ambientops — the `+.sh+` + `+.desktop+` file pair is the +contract. Idaptik’s `+.desktop+` also declares itself as a URI handler +(`+MimeType=x-scheme-handler/idaptik;+` with `+Exec=… %u+`), so the +launcher receives a `+%u+` URI argument from the desktop environment on +activation and must route it. The `+.sh+` additionally owns two +display-front-end choices (`+--gossamer+` and `+--tray+`) that the +standard `+--browser+` arm does not cover, plus a pre-start port check +(`+lsof -i :8080+`). |`+--browser+`, `+--web+`, `+--gossamer+`, +`+--tray+`, plus `+%u+` URI handling from the `+.desktop+` file and +`+lsof+`-based port pre-check |`+launch-scaffolder+` needs (a) a +*URI-handler manifest field* that emits the correct `+MimeType=+`, +`+Exec=… %u+` and argv handling in the `+.sh+`, (b) a declarable list of +*alternative display front-ends* (`+browser+` / `+gossamer+` / `+tray+` +/ …) with a fallback chain, and (c) an optional +`+shape: server-with-url+` augmentation for a pre-start port-conflict +probe via `+lsof+` or `+ss+`. +|=== + +=== Scaffolder-managed subset (for completeness) + +The 11 launchers minus the 5 exceptions above leave *6 launchers* that +the parallel session’s scaffolder minting pass can own today: + +[width="100%",cols="34%,33%,33%",options="header",] +|=== +|Launcher |Runtime shape |Notes +|`+aerie-launcher.sh+` |background-process |Wraps +`+repo-quicklaunch.sh → just tour+`. Hardcoded `+/home/hyper/Desktop/…+` +path to fix. + +|`+burble-launcher.sh+` |server-with-url (port 4020, Phoenix) |Clean +template fit — already most like the reference. + +|`+game-server-admin-launcher.sh+` |background-process |Needs the +double-indirection to `+~/.local/bin/game-server-admin-launcher+` +untangled before minting. + +|`+nqc-launcher.sh+` |background-process |Hardcoded +`+/home/hyper/.bin/nqc+`. No `+REPO_DIR+`. + +|`+panll-launcher.sh+` |server-with-url (port 8000) |Accepts bare-word +aliases (`+serve+`, `+start+`, …) alongside `+--foo+` forms; scaffolder +should decide whether to preserve that. + +|`+project-wharf-launcher.sh+` |background-process |Same hardcoded +`+repo-quicklaunch.sh+` path as aerie/ambientops. +|=== + +These six collectively need only `+--integ+`/`+--disinteg+`/explicit +`+--help+` added and the hardcoded `+$HOME+` paths parameterised — no +new runtime shapes, no `+.desktop+` Actions, no URI handlers, no +shell-context daemons. That matches what the compliance audit flagged as +the fleet-level gaps. + +=== Cross-reference against the compliance audit + +The compliance audit was written before the exception list was declared. +Reconciling the two surfaces the following discrepancies, flagged here +rather than silently corrected in the audit. + +==== Discrepancy 1 — opsm runtime-shape classification + +* *Audit says:* in the fleet-level runtime-shape taxonomy table, +`+opsm-launcher.sh+` is listed under `+background-process+` alongside +aerie, ambientops, game-server-admin, nqc, project-wharf (6/11). +* *Audit also says* (finding #8, narrative): the tracked PID is actually +an `+exec bash+` login shell, not a real daemon, and the scaffolder +should treat it as a "`distinct runtime shape`". +* *Exception list says:* opsm is explicitly a shell-context launcher, +not a daemon. +* *Reconciliation:* the exception list is authoritative. The audit’s +taxonomy row for opsm is wrong — `+opsm+` belongs to a fifth runtime +shape (`+shell-context+`) that the audit narrated but did not add to its +4-shape table. After reclassification the corrected counts are: +`+background-process+` = 5 (not 6), `+server-with-url+` = 3, +`+remote-web-app+` = 1, `+one-shot-cli+` = 1, `+shell-context+` = 1. +*Not patching the audit file*; this note records the correction. + +==== Discrepancy 2 — `+.desktop+` file scope gap + +* *Audit scope:* the audit read only the `+*-launcher.sh+` files. It did +not open any `+.desktop+` files. +* *Exception list asserts* that two launchers (ambientops, idaptik) +cannot be scaffolder-managed _because of_ `+.desktop+` file content — +Action entries for ambientops, URI-handler + `+%u+` for idaptik. +* *Reconciliation:* not a contradiction; an audit-scope gap. The audit +did not make any claim about `+.desktop+` files at all, so the exception +list extends rather than contradicts it. Flag: a follow-up audit should +read the paired `+.desktop+` files before the scaffolder emits anything +for ambientops or idaptik, and the scaffolder’s per-app manifest schema +needs `+.desktop+` Actions + URI-handler fields before either launcher +can be migrated. + +==== Discrepancy 3 — invariant-path extras list is a narrower summary + +* *Audit extras list:* `+--scan+`, `+--scan-file+`, `+--cli+`, +`+--open-output+` (four extras beyond the standard modes). +* *Exception list summary:* +`+--scan / --cli / --status / --auto → cargo run dispatch+` +(three-extras-plus-auto framing). +* *Reconciliation:* not a contradiction; the exception list is a +higher-level summary and omits `+--scan-file+` and `+--open-output+` for +brevity. *Both modes must be preserved* in the scaffolder’s eventual +`+one-shot-cli+` template, and the exception row above explicitly +records them. + +==== Discrepancy 4 — opsm extras + +* *Audit extras list:* `+--system-update+`. +* *Exception list summary:* "`shell-context launcher, not a daemon`" (no +explicit mention of `+--system-update+`). +* *Reconciliation:* not a contradiction; the exception list describes +the _shape_, the audit describes the _mode surface_. The exception row +above preserves `+--system-update+` as a required custom mode so the +eventual `+shell-context+` manifest has to declare it. + +==== Discrepancy 5 — hypatia extras alignment + +* *Audit extras list:* `+--tour+`, `+--gui+`, `+--local+`, `+--dev+`, +`+--cli+`, `+--scan+`. +* *Exception list summary:* +`+--tour / --gui / --local / --dev / --cli / --scan + gossamer exec+`. +* *Reconciliation:* matching. No discrepancy. The gossamer `+exec+` +dispatch was called out in the audit narrative but not in its extras +column; the exception row above hoists it back to first-class status as +a required capability. + +==== Discrepancy 6 — ambientops extras alignment + +* *Audit extras list:* `+--toggle-input-devices+`, +`+--emergency-input-restore+`. +* *Exception list summary:* matches exactly. +* *Reconciliation:* no discrepancy on the `+.sh+` side. The `+.desktop+` +side is covered by Discrepancy 2 above. + +==== Discrepancy 7 — idaptik extras alignment + +* *Audit extras list:* `+--web+`, `+--gossamer+`, `+--tray+`. +* *Exception list summary:* `+--gossamer+`, `+--tray+`, plus `+%u+` +handling. `+--web+` (an alias of `+--browser+`) is not mentioned. +* *Reconciliation:* `+--web+` is a trivial alias the scaffolder can emit +automatically for any `+server-with-url+` shape. `+%u+` handling is +covered by Discrepancy 2. No substantive contradiction. + +=== Summary + +* *5 launchers declared out of scope* for `+launch-scaffolder+` minting: +hypatia, invariant-path, opsm, ambientops, idaptik. +* *6 launchers remain in scope:* aerie, burble, game-server-admin, nqc, +panll, project-wharf. +* *Cross-reference against the compliance audit:* 7 reconciliation +notes, 1 material correction (opsm runtime-shape), 1 scope gap +(`+.desktop+` files not inspected), 0 hard contradictions. +* *Scaffolder template-engine gaps implied by the exceptions*, in +priority order: +[arabic] +. `+shape: one-shot-cli+` (invariant-path) +. `+shape: remote-web-app+` with conditional gossamer/browser dispatch +(hypatia) +. `+.desktop+` Actions + URI-handler manifest fields (ambientops, +idaptik) +. `+shape: shell-context+` (opsm — or deletion) +. Pre-start `+lsof+`/`+ss+` port probe for `+server-with-url+` (idaptik; +also surfaces in the audit for burble/panll as a recommended standard +pattern) + +None of the in-scope 6 launchers need any of the above before they can +be minted, so the parallel session’s current scaffolder-minting pass is +not blocked by this document. diff --git a/docs/launcher-exceptions-2026-04-10.md b/docs/launcher-exceptions-2026-04-10.md deleted file mode 100644 index f9be732..0000000 --- a/docs/launcher-exceptions-2026-04-10.md +++ /dev/null @@ -1,157 +0,0 @@ - -# Launcher Scaffolder Exceptions — 2026-04-10 - -Five launchers in `/var/mnt/eclipse/repos/.desktop-tools/` are declared -**out of scope for `launch-scaffolder` minting** as of 2026-04-10. They -stay hand-written for concrete reasons captured below. Each row records -what would have to change in `launch-scaffolder` before the launcher -could be moved into scaffolder management. - -Companion document: [`compliance-audit-2026-04-10.md`](compliance-audit-2026-04-10.md). -See [§Cross-reference against the compliance audit](#cross-reference-against-the-compliance-audit) -at the end for the reconciliation notes — discrepancies are flagged, not -silently patched. - -## Exceptions table - -| Launcher | Current location | Reason it can't be scaffolder-managed | Extra / custom modes exposed beyond the standard template | Migration trigger (what would have to land in `launch-scaffolder` first) | -|---|---|---|---|---| -| `hypatia-launcher.sh` | `/var/mnt/eclipse/repos/.desktop-tools/` | Remote web app (`https://nesy-prover.dev`) with a **conditional** front-end: if `gossamer` is on `PATH` *and* `~/.config/hypatia/gossamer.conf.json` exists, `exec gossamer`; otherwise fall through to `xdg-open`/`firefox`. No daemon, no PID file, no `start`/`stop` semantics. The `--cli` mode `exec`s a separate script (`hypatia-cli.sh`) and is a different runtime shape again. | `--tour`, `--gui`, `--local`, `--dev`, `--cli`, `--scan`, plus `exec gossamer …` dispatch | `launch-scaffolder` needs a `shape: remote-web-app` runtime in its template engine that (a) emits no daemon machinery, (b) supports a conditional gossamer-vs-browser dispatch block, and (c) allows a sibling `exec`-delegated CLI subcommand (`--cli`/`--scan`) to live in the same launcher. | -| `invariant-path-launcher.sh` | `/var/mnt/eclipse/repos/.desktop-tools/` | Bespoke Rust-CLI wrapper: every invocation shells out to `cargo run --manifest-path … -p invariant-path-cli --`, writes results to `/tmp/invariant-path-last-scan.json`, and exits. Not a server, not a daemon, no readiness probe, no browser. `--status` reports the last scan output, not a running process. Also owns its own `--scan-file` and `--open-output` modes that touch a well-known output file. | `--scan [repo] [profile]`, `--scan-file [profile]`, `--cli ` (pass-through), `--open-output`, `--status` (reports last-scan summary), `--auto` (scan default repo) — all dispatched through `cargo run` | `launch-scaffolder` needs a `shape: one-shot-cli` runtime that emits (a) no PID file, (b) a `cargo run` / `just` / `deno task` dispatcher with configurable backend, (c) a well-known `OUTPUT_FILE` contract, (d) an optional `jq`-based summariser, and (e) a `--status` that reports "last run output" instead of "process state". | -| `opsm-launcher.sh` | `/var/mnt/eclipse/repos/.desktop-tools/` | Its runtime is **a login shell**. The "daemon" is `nohup bash -lc "$COMMAND_SCRIPT"` where `$COMMAND_SCRIPT` sources `~/.bashrc.d/tools/opsm`, runs `opsm-runtime list && opsm-runtime doctor`, and ends in `exec bash`. The tracked PID is therefore an interactive bash session, not the real OPSM runtime. Also depends on a profile fragment that only exists on the author's machine, so the launcher is non-portable by construction. | `--system-update` (custom), plus the implicit "source profile → `exec bash`" behaviour of `--start` / `--auto` | `launch-scaffolder` needs a `shape: shell-context` runtime that (a) deliberately does **not** produce a PID-file-tracked daemon, (b) runs the command synchronously in a foreground login shell, (c) declares its profile-source dependencies in the per-app manifest so a dependency-check step can detect missing fragments, and (d) supports arbitrary extra modes like `--system-update` as first-class entries in the manifest. Alternative: the user decides OPSM should not have a launcher at all and is invoked interactively only, in which case this file is deleted rather than migrated. | -| `ambientops-launcher.sh` | `/var/mnt/eclipse/repos/.desktop-tools/` | Not just the `.sh` — its companion `.desktop` file exposes input-device management as `Desktop Action` entries, so the launcher contract is **file-pair** (`.sh` + `.desktop`), not just the shell script. The two custom `.sh` modes (`--toggle-input-devices`, `--emergency-input-restore`) are there specifically to be reachable from the Action entries in the `.desktop` file. | `--toggle-input-devices`, `--emergency-input-restore`, both called via `repo-quicklaunch.sh … just ` | `launch-scaffolder` needs first-class support for **extra `.desktop` `[Desktop Action …]` entries** declared in the per-app manifest, and the ability to bind each Action to a custom mode name in the emitted `.sh`. Today the scaffolder only emits the stock `stop;status;` Action pair described in `launcher-standard.adoc §Desktop File Standard`; it cannot emit arbitrary additional Actions. | -| `idaptik-launcher.sh` | `/var/mnt/eclipse/repos/.desktop-tools/` | Similar to ambientops — the `.sh` + `.desktop` file pair is the contract. Idaptik's `.desktop` also declares itself as a URI handler (`MimeType=x-scheme-handler/idaptik;` with `Exec=… %u`), so the launcher receives a `%u` URI argument from the desktop environment on activation and must route it. The `.sh` additionally owns two display-front-end choices (`--gossamer` and `--tray`) that the standard `--browser` arm does not cover, plus a pre-start port check (`lsof -i :8080`). | `--browser`, `--web`, `--gossamer`, `--tray`, plus `%u` URI handling from the `.desktop` file and `lsof`-based port pre-check | `launch-scaffolder` needs (a) a **URI-handler manifest field** that emits the correct `MimeType=`, `Exec=… %u` and argv handling in the `.sh`, (b) a declarable list of **alternative display front-ends** (`browser` / `gossamer` / `tray` / …) with a fallback chain, and (c) an optional `shape: server-with-url` augmentation for a pre-start port-conflict probe via `lsof` or `ss`. | - -## Scaffolder-managed subset (for completeness) - -The 11 launchers minus the 5 exceptions above leave **6 launchers** that -the parallel session's scaffolder minting pass can own today: - -| Launcher | Runtime shape | Notes | -|---|---|---| -| `aerie-launcher.sh` | background-process | Wraps `repo-quicklaunch.sh → just tour`. Hardcoded `/home/hyper/Desktop/…` path to fix. | -| `burble-launcher.sh` | server-with-url (port 4020, Phoenix) | Clean template fit — already most like the reference. | -| `game-server-admin-launcher.sh` | background-process | Needs the double-indirection to `~/.local/bin/game-server-admin-launcher` untangled before minting. | -| `nqc-launcher.sh` | background-process | Hardcoded `/home/hyper/.bin/nqc`. No `REPO_DIR`. | -| `panll-launcher.sh` | server-with-url (port 8000) | Accepts bare-word aliases (`serve`, `start`, …) alongside `--foo` forms; scaffolder should decide whether to preserve that. | -| `project-wharf-launcher.sh` | background-process | Same hardcoded `repo-quicklaunch.sh` path as aerie/ambientops. | - -These six collectively need only `--integ`/`--disinteg`/explicit `--help` -added and the hardcoded `$HOME` paths parameterised — no new runtime -shapes, no `.desktop` Actions, no URI handlers, no shell-context daemons. -That matches what the compliance audit flagged as the fleet-level gaps. - -## Cross-reference against the compliance audit - -The compliance audit was written before the exception list was declared. -Reconciling the two surfaces the following discrepancies, flagged here -rather than silently corrected in the audit. - -### Discrepancy 1 — opsm runtime-shape classification - -- **Audit says:** in the fleet-level runtime-shape taxonomy table, - `opsm-launcher.sh` is listed under `background-process` alongside - aerie, ambientops, game-server-admin, nqc, project-wharf (6/11). -- **Audit also says** (finding #8, narrative): the tracked PID is - actually an `exec bash` login shell, not a real daemon, and the - scaffolder should treat it as a "distinct runtime shape". -- **Exception list says:** opsm is explicitly a shell-context launcher, - not a daemon. -- **Reconciliation:** the exception list is authoritative. The audit's - taxonomy row for opsm is wrong — `opsm` belongs to a fifth runtime - shape (`shell-context`) that the audit narrated but did not add to - its 4-shape table. After reclassification the corrected counts are: - `background-process` = 5 (not 6), `server-with-url` = 3, - `remote-web-app` = 1, `one-shot-cli` = 1, `shell-context` = 1. - **Not patching the audit file**; this note records the correction. - -### Discrepancy 2 — `.desktop` file scope gap - -- **Audit scope:** the audit read only the `*-launcher.sh` files. It - did not open any `.desktop` files. -- **Exception list asserts** that two launchers (ambientops, idaptik) - cannot be scaffolder-managed *because of* `.desktop` file content — - Action entries for ambientops, URI-handler + `%u` for idaptik. -- **Reconciliation:** not a contradiction; an audit-scope gap. The - audit did not make any claim about `.desktop` files at all, so the - exception list extends rather than contradicts it. Flag: a follow-up - audit should read the paired `.desktop` files before the scaffolder - emits anything for ambientops or idaptik, and the scaffolder's - per-app manifest schema needs `.desktop` Actions + URI-handler fields - before either launcher can be migrated. - -### Discrepancy 3 — invariant-path extras list is a narrower summary - -- **Audit extras list:** `--scan`, `--scan-file`, `--cli`, `--open-output` - (four extras beyond the standard modes). -- **Exception list summary:** `--scan / --cli / --status / --auto → - cargo run dispatch` (three-extras-plus-auto framing). -- **Reconciliation:** not a contradiction; the exception list is a - higher-level summary and omits `--scan-file` and `--open-output` - for brevity. **Both modes must be preserved** in the scaffolder's - eventual `one-shot-cli` template, and the exception row above - explicitly records them. - -### Discrepancy 4 — opsm extras - -- **Audit extras list:** `--system-update`. -- **Exception list summary:** "shell-context launcher, not a daemon" - (no explicit mention of `--system-update`). -- **Reconciliation:** not a contradiction; the exception list describes - the *shape*, the audit describes the *mode surface*. The exception row - above preserves `--system-update` as a required custom mode so the - eventual `shell-context` manifest has to declare it. - -### Discrepancy 5 — hypatia extras alignment - -- **Audit extras list:** `--tour`, `--gui`, `--local`, `--dev`, `--cli`, - `--scan`. -- **Exception list summary:** `--tour / --gui / --local / --dev / --cli / - --scan + gossamer exec`. -- **Reconciliation:** matching. No discrepancy. The gossamer `exec` - dispatch was called out in the audit narrative but not in its extras - column; the exception row above hoists it back to first-class status - as a required capability. - -### Discrepancy 6 — ambientops extras alignment - -- **Audit extras list:** `--toggle-input-devices`, - `--emergency-input-restore`. -- **Exception list summary:** matches exactly. -- **Reconciliation:** no discrepancy on the `.sh` side. The `.desktop` - side is covered by Discrepancy 2 above. - -### Discrepancy 7 — idaptik extras alignment - -- **Audit extras list:** `--web`, `--gossamer`, `--tray`. -- **Exception list summary:** `--gossamer`, `--tray`, plus `%u` - handling. `--web` (an alias of `--browser`) is not mentioned. -- **Reconciliation:** `--web` is a trivial alias the scaffolder can - emit automatically for any `server-with-url` shape. `%u` handling is - covered by Discrepancy 2. No substantive contradiction. - -## Summary - -- **5 launchers declared out of scope** for `launch-scaffolder` minting: - hypatia, invariant-path, opsm, ambientops, idaptik. -- **6 launchers remain in scope:** aerie, burble, game-server-admin, - nqc, panll, project-wharf. -- **Cross-reference against the compliance audit:** 7 reconciliation - notes, 1 material correction (opsm runtime-shape), 1 scope gap - (`.desktop` files not inspected), 0 hard contradictions. -- **Scaffolder template-engine gaps implied by the exceptions**, in - priority order: - 1. `shape: one-shot-cli` (invariant-path) - 2. `shape: remote-web-app` with conditional gossamer/browser dispatch (hypatia) - 3. `.desktop` Actions + URI-handler manifest fields (ambientops, idaptik) - 4. `shape: shell-context` (opsm — or deletion) - 5. Pre-start `lsof`/`ss` port probe for `server-with-url` (idaptik; - also surfaces in the audit for burble/panll as a recommended - standard pattern) - -None of the in-scope 6 launchers need any of the above before they can -be minted, so the parallel session's current scaffolder-minting pass is -not blocked by this document. diff --git a/docs/ruleset-audit-2026-04-10/README.adoc b/docs/ruleset-audit-2026-04-10/README.adoc new file mode 100644 index 0000000..3556061 --- /dev/null +++ b/docs/ruleset-audit-2026-04-10/README.adoc @@ -0,0 +1,122 @@ +== `+docs/ruleset-audit-2026-04-10/+` + +Raw data and scripts from the estate-wide branch-protection ruleset +audit conducted on 2026-04-10. The human-readable post-mortem lives at +link:../branch-protection-remediation-2026-04-10.md[`+../branch-protection-remediation-2026-04-10.md+`] +— *read that first*. + +=== Contents + +[width="100%",cols="50%,50%",options="header",] +|=== +|File |Description +|`+audit.sh+` |Read-only classifier. `+bash audit.sh+` re-runs the audit +and rewrites `+report.jsonl+`. Uses `+gh api+` — requires an +authenticated GitHub CLI session. + +|`+reference-ruleset.json+` |Canonical 5-rule `+Base+` ruleset body. The +one POST body used to create/recreate every compliant ruleset in Wave 1. + +|`+repos.tsv+` |`+\t+` — the 315 non-archived +`+hyperpolymath/*+` repos enumerated at audit time. + +|`+forks.txt+` |The 8 upstream forks inside `+hyperpolymath/*+`. Used to +exclude forks from Wave 1 per an explicit user decision. + +|`+report.jsonl+` |One JSON record per repo from the audit run: +`+{repo, state, ...}+` where `+state ∈ {OK, DRIFT, MISSING, ERROR}+`. + +|`+wave1-apply.sh+` |Wave 1 creator for `+MISSING+` repos (POST +reference ruleset). Supports `+OWNER+` override and `+--dry-run+` plan +generation. + +|`+wave1-repos.txt+` |The 29 repos classified as MISSING +(pre-fork-exclusion). + +|`+wave1-apply.txt+` |The 21 repos that actually received Wave 1 writes +(29 MISSING minus 8 forks). + +|`+wave1-plan.jsonl+` |The dry-run plan file. One line per planned API +call — `+{method, path, body_file, repo}+`. + +|`+wave1-results.tsv+` |Per-repo apply outcome. All 21 now show `+OK+` +with their `+ruleset_id+`. +|=== + +=== Re-running the audit + +[source,bash] +---- +# From launch-scaffolder/ root. +cd docs/ruleset-audit-2026-04-10/ + +# Refresh the repo list (optional — only needed if new repos have +# been added or repos have been archived since the last run): +OWNER=hyperpolymath +gh repo list "$OWNER" --limit 500 --no-archived \ + --json name,isPrivate --jq '.[] | "\(.name)\t\(.isPrivate)"' > repos.tsv + +# Re-audit: +OWNER="$OWNER" bash audit.sh + +# Summarise drift: +jq -r .state report.jsonl | sort | uniq -c +---- + +=== Re-applying to a new batch (e.g. Wave 2) + +[source,bash] +---- +# Pick your wave — DRIFT this time: +jq -r 'select(.state=="DRIFT") | .repo' report.jsonl > /tmp/wave2-repos.txt + +# Exclude forks: +comm -23 <(sort /tmp/wave2-repos.txt) forks.txt > /tmp/wave2-apply.txt + +# For Wave 2 the existing (broken) ruleset must be deleted first, +# because POST will fail with "Name must be unique" on every repo. +# The two-step is: GET the existing Base ruleset id, DELETE it, +# then POST the reference body. Script this only after a dry-run +# review of the plan file. + +# Dry-run with alternate owner: +OWNER=The-Metadatastician \ +REPOS_FILE=/tmp/wave2-apply.txt \ +bash wave2-apply.sh --dry-run +---- + +=== Wave 1 (MISSING repos) + +[source,bash] +---- +# Build Wave 1 repo list from current report: +jq -r 'select(.state=="MISSING") | .repo' report.jsonl > /tmp/wave1-repos.txt + +# Dry-run / plan generation: +OWNER=The-Metadatastician \ +REPOS_FILE=/tmp/wave1-repos.txt \ +PLAN_FILE=/tmp/ruleset-audit/wave1-plan-The-Metadatastician.jsonl \ +RESULTS_FILE=/tmp/ruleset-audit/wave1-results-The-Metadatastician.tsv \ +bash wave1-apply.sh --dry-run + +# Apply: +OWNER=The-Metadatastician \ +REPOS_FILE=/tmp/wave1-repos.txt \ +PLAN_FILE=/tmp/ruleset-audit/wave1-plan-The-Metadatastician.jsonl \ +RESULTS_FILE=/tmp/ruleset-audit/wave1-results-The-Metadatastician.tsv \ +bash wave1-apply.sh +---- + +=== Why the artefacts live here, not in `+~/security-fixes/+` + +Per global memory, batch fix scripts usually live under +`+~/security-fixes/+` (e.g. `+fix-permissions.jl+`, +`+enable-branch-protection.jl+`). That directory didn’t exist at the +time of this audit. Rather than create a new top-level location with one +set of artefacts in it, the Wave 1 data lives alongside its post-mortem +in `+launch-scaffolder/docs/+` — the scaffolder was the tool that +surfaced the drift problem when `+cmd_provision+` was being pushed to +downstream repos, and its own `+launch-scaffolder+` repo was in the +MISSING list. A future Wave 2 script may move under +`+~/security-fixes/+` or `+ambientops/+` once the estate-wide batch-fix +pattern has more than one instance and the right home is obvious. diff --git a/docs/ruleset-audit-2026-04-10/README.md b/docs/ruleset-audit-2026-04-10/README.md deleted file mode 100644 index 3884965..0000000 --- a/docs/ruleset-audit-2026-04-10/README.md +++ /dev/null @@ -1,99 +0,0 @@ - -# `docs/ruleset-audit-2026-04-10/` - -Raw data and scripts from the estate-wide branch-protection ruleset -audit conducted on 2026-04-10. The human-readable post-mortem lives -at [`../branch-protection-remediation-2026-04-10.md`](../branch-protection-remediation-2026-04-10.md) — **read that first**. - -## Contents - -| File | Description | -|---|---| -| `audit.sh` | Read-only classifier. `bash audit.sh` re-runs the audit and rewrites `report.jsonl`. Uses `gh api` — requires an authenticated GitHub CLI session. | -| `reference-ruleset.json` | Canonical 5-rule `Base` ruleset body. The one POST body used to create/recreate every compliant ruleset in Wave 1. | -| `repos.tsv` | `\t` — the 315 non-archived `hyperpolymath/*` repos enumerated at audit time. | -| `forks.txt` | The 8 upstream forks inside `hyperpolymath/*`. Used to exclude forks from Wave 1 per an explicit user decision. | -| `report.jsonl` | One JSON record per repo from the audit run: `{repo, state, ...}` where `state ∈ {OK, DRIFT, MISSING, ERROR}`. | -| `wave1-apply.sh` | Wave 1 creator for `MISSING` repos (POST reference ruleset). Supports `OWNER` override and `--dry-run` plan generation. | -| `wave1-repos.txt` | The 29 repos classified as MISSING (pre-fork-exclusion). | -| `wave1-apply.txt` | The 21 repos that actually received Wave 1 writes (29 MISSING minus 8 forks). | -| `wave1-plan.jsonl` | The dry-run plan file. One line per planned API call — `{method, path, body_file, repo}`. | -| `wave1-results.tsv` | Per-repo apply outcome. All 21 now show `OK` with their `ruleset_id`. | - -## Re-running the audit - -```bash -# From launch-scaffolder/ root. -cd docs/ruleset-audit-2026-04-10/ - -# Refresh the repo list (optional — only needed if new repos have -# been added or repos have been archived since the last run): -OWNER=hyperpolymath -gh repo list "$OWNER" --limit 500 --no-archived \ - --json name,isPrivate --jq '.[] | "\(.name)\t\(.isPrivate)"' > repos.tsv - -# Re-audit: -OWNER="$OWNER" bash audit.sh - -# Summarise drift: -jq -r .state report.jsonl | sort | uniq -c -``` - -## Re-applying to a new batch (e.g. Wave 2) - -```bash -# Pick your wave — DRIFT this time: -jq -r 'select(.state=="DRIFT") | .repo' report.jsonl > /tmp/wave2-repos.txt - -# Exclude forks: -comm -23 <(sort /tmp/wave2-repos.txt) forks.txt > /tmp/wave2-apply.txt - -# For Wave 2 the existing (broken) ruleset must be deleted first, -# because POST will fail with "Name must be unique" on every repo. -# The two-step is: GET the existing Base ruleset id, DELETE it, -# then POST the reference body. Script this only after a dry-run -# review of the plan file. - -# Dry-run with alternate owner: -OWNER=The-Metadatastician \ -REPOS_FILE=/tmp/wave2-apply.txt \ -bash wave2-apply.sh --dry-run -``` - -## Wave 1 (MISSING repos) - -```bash -# Build Wave 1 repo list from current report: -jq -r 'select(.state=="MISSING") | .repo' report.jsonl > /tmp/wave1-repos.txt - -# Dry-run / plan generation: -OWNER=The-Metadatastician \ -REPOS_FILE=/tmp/wave1-repos.txt \ -PLAN_FILE=/tmp/ruleset-audit/wave1-plan-The-Metadatastician.jsonl \ -RESULTS_FILE=/tmp/ruleset-audit/wave1-results-The-Metadatastician.tsv \ -bash wave1-apply.sh --dry-run - -# Apply: -OWNER=The-Metadatastician \ -REPOS_FILE=/tmp/wave1-repos.txt \ -PLAN_FILE=/tmp/ruleset-audit/wave1-plan-The-Metadatastician.jsonl \ -RESULTS_FILE=/tmp/ruleset-audit/wave1-results-The-Metadatastician.tsv \ -bash wave1-apply.sh -``` - -## Why the artefacts live here, not in `~/security-fixes/` - -Per global memory, batch fix scripts usually live under -`~/security-fixes/` (e.g. `fix-permissions.jl`, -`enable-branch-protection.jl`). That directory didn't exist at the -time of this audit. Rather than create a new top-level location with -one set of artefacts in it, the Wave 1 data lives alongside its -post-mortem in `launch-scaffolder/docs/` — the scaffolder was the -tool that surfaced the drift problem when `cmd_provision` was being -pushed to downstream repos, and its own `launch-scaffolder` repo was -in the MISSING list. A future Wave 2 script may move under -`~/security-fixes/` or `ambientops/` once the estate-wide batch-fix -pattern has more than one instance and the right home is obvious. diff --git a/docs/ruleset-audit-2026-04-10/TRANSFER-VERIFICATION-CHECKLIST-2026-04-22.adoc b/docs/ruleset-audit-2026-04-10/TRANSFER-VERIFICATION-CHECKLIST-2026-04-22.adoc new file mode 100644 index 0000000..c13cbb2 --- /dev/null +++ b/docs/ruleset-audit-2026-04-10/TRANSFER-VERIFICATION-CHECKLIST-2026-04-22.adoc @@ -0,0 +1,99 @@ +== Transfer Verification Checklist — 2026-04-22 + +Scope: verify whether the following legacy repos were transferred from +`+hyperpolymath+` to `+The-Metadatastician+` (possibly renamed): + +[arabic] +. `+repos-monorepo+` +. `+.git-private-farm+` +. `+blog-drafts+` +. `+hyperpolymath-sovereign-registry+` + +=== Current evidence (captured 2026-04-22) + +[width="100%",cols="20%,20%,20%,20%,20%",options="header",] +|=== +|Legacy repo |Exists at `+hyperpolymath/+` |Visibility there +|Exists at `+The-Metadatastician/+` |Rules endpoint at source +|`+repos-monorepo+` |Yes (`+200+`) |`+private+` |No (`+404+`) |`+403+` +plan-gated + +|`+.git-private-farm+` |Yes (`+200+`) |`+private+` |No (`+404+`) +|`+403+` plan-gated + +|`+blog-drafts+` |Yes (`+200+`) |`+private+` |No (`+404+`) |`+403+` +plan-gated + +|`+hyperpolymath-sovereign-registry+` |Yes (`+200+`) |`+private+` |No +(`+404+`) |`+403+` plan-gated +|=== + +Interpretation: these four repositories are currently still under the +`+hyperpolymath+` user account with private visibility, and are not +present under `+The-Metadatastician+` with the same names. + +=== Verification checklist + +* [x] Confirm source-owner existence and visibility. +* [x] Confirm target-owner same-name existence (`+404+` for all four). +* [x] Check whether they were transferred with the same names in target +org. +** Result: no same-name matches under `+The-Metadatastician+`. +* [x] Check whether they were transferred and renamed in target org. +** Result: no credible rename candidates among the current 8 +repositories in `+The-Metadatastician+` inventory (see +`+transfer-map-2026-04-22.tsv+`). +** Export org repo inventory: ++ +[source,bash] +---- +gh repo list The-Metadatastician --limit 500 --json name,nameWithOwner,visibility,isPrivate \ + | jq -r '.[] | [.name, .nameWithOwner, .visibility, (.isPrivate|tostring)] | @tsv' \ + > /tmp/the-metadatastician-repos.tsv +---- +** Manually map candidate renames from +`+/tmp/the-metadatastician-repos.tsv+`. +* [ ] Verify transfer event(s) in org audit log for each legacy repo +name. +** Example query (adjust phrase as needed): ++ +[source,bash] +---- +gh api 'orgs/The-Metadatastician/audit-log?phrase=repo%20transfer&per_page=100' +---- +* [ ] Verify source-side repository redirect behavior for each legacy +repo. +** If transferred, GitHub usually redirects old clone URLs to new +owner/name. +** Example: ++ +[source,bash] +---- +git ls-remote https://github.com/hyperpolymath/repos-monorepo.git +---- +* [x] Produce and store an explicit mapping file: +** `+docs/ruleset-audit-2026-04-10/transfer-map-2026-04-22.tsv+` with +columns: +*** `+legacy_owner+` +*** `+legacy_repo+` +*** `+new_owner+` +*** `+new_repo+` +*** `+status+` (`+confirmed+` or `+needs-review+`) +*** `+evidence+` +* [x] Update automation inputs to match the confirmed owner/name map. +** Ruleset audit owner override: ++ +[source,bash] +---- +OWNER=The-Metadatastician bash docs/ruleset-audit-2026-04-10/audit.sh +---- +** `+audit.sh+` now accepts `+OWNER+`, `+REPOS_FILE+`, and +`+REPORT_FILE+` env overrides. + +=== Exit criteria + +* Every legacy repo has either: +** a confirmed transfer+rename mapping in `+transfer-map.tsv+`, or +** a confirmed "`not transferred`" disposition. +* Ruleset audits target the correct owner/repo namespace with no +ambiguity. diff --git a/docs/ruleset-audit-2026-04-10/TRANSFER-VERIFICATION-CHECKLIST-2026-04-22.md b/docs/ruleset-audit-2026-04-10/TRANSFER-VERIFICATION-CHECKLIST-2026-04-22.md deleted file mode 100644 index d94456f..0000000 --- a/docs/ruleset-audit-2026-04-10/TRANSFER-VERIFICATION-CHECKLIST-2026-04-22.md +++ /dev/null @@ -1,75 +0,0 @@ - -# Transfer Verification Checklist — 2026-04-22 - -Scope: verify whether the following legacy repos were transferred from -`hyperpolymath` to `The-Metadatastician` (possibly renamed): - -1. `repos-monorepo` -2. `.git-private-farm` -3. `blog-drafts` -4. `hyperpolymath-sovereign-registry` - -## Current evidence (captured 2026-04-22) - -| Legacy repo | Exists at `hyperpolymath/` | Visibility there | Exists at `The-Metadatastician/` | Rules endpoint at source | -|---|---|---|---|---| -| `repos-monorepo` | Yes (`200`) | `private` | No (`404`) | `403` plan-gated | -| `.git-private-farm` | Yes (`200`) | `private` | No (`404`) | `403` plan-gated | -| `blog-drafts` | Yes (`200`) | `private` | No (`404`) | `403` plan-gated | -| `hyperpolymath-sovereign-registry` | Yes (`200`) | `private` | No (`404`) | `403` plan-gated | - -Interpretation: these four repositories are currently still under the -`hyperpolymath` user account with private visibility, and are not present -under `The-Metadatastician` with the same names. - -## Verification checklist - -- [x] Confirm source-owner existence and visibility. -- [x] Confirm target-owner same-name existence (`404` for all four). -- [x] Check whether they were transferred with the same names in target org. - - Result: no same-name matches under `The-Metadatastician`. -- [x] Check whether they were transferred and renamed in target org. - - Result: no credible rename candidates among the current 8 repositories in - `The-Metadatastician` inventory (see `transfer-map-2026-04-22.tsv`). - - Export org repo inventory: - ```bash - gh repo list The-Metadatastician --limit 500 --json name,nameWithOwner,visibility,isPrivate \ - | jq -r '.[] | [.name, .nameWithOwner, .visibility, (.isPrivate|tostring)] | @tsv' \ - > /tmp/the-metadatastician-repos.tsv - ``` - - Manually map candidate renames from `/tmp/the-metadatastician-repos.tsv`. -- [ ] Verify transfer event(s) in org audit log for each legacy repo name. - - Example query (adjust phrase as needed): - ```bash - gh api 'orgs/The-Metadatastician/audit-log?phrase=repo%20transfer&per_page=100' - ``` -- [ ] Verify source-side repository redirect behavior for each legacy repo. - - If transferred, GitHub usually redirects old clone URLs to new owner/name. - - Example: - ```bash - git ls-remote https://github.com/hyperpolymath/repos-monorepo.git - ``` -- [x] Produce and store an explicit mapping file: - - `docs/ruleset-audit-2026-04-10/transfer-map-2026-04-22.tsv` with columns: - - `legacy_owner` - - `legacy_repo` - - `new_owner` - - `new_repo` - - `status` (`confirmed` or `needs-review`) - - `evidence` -- [x] Update automation inputs to match the confirmed owner/name map. - - Ruleset audit owner override: - ```bash - OWNER=The-Metadatastician bash docs/ruleset-audit-2026-04-10/audit.sh - ``` - - `audit.sh` now accepts `OWNER`, `REPOS_FILE`, and `REPORT_FILE` env overrides. - -## Exit criteria - -- Every legacy repo has either: - - a confirmed transfer+rename mapping in `transfer-map.tsv`, or - - a confirmed "not transferred" disposition. -- Ruleset audits target the correct owner/repo namespace with no ambiguity. diff --git a/docs/tech-debt-2026-05-26.adoc b/docs/tech-debt-2026-05-26.adoc new file mode 100644 index 0000000..b35ee25 --- /dev/null +++ b/docs/tech-debt-2026-05-26.adoc @@ -0,0 +1,71 @@ +== Tech-Debt Audit — launch-scaffolder — 2026-05-26 + +*Source:* estate-wide automated scan 2026-05-26. *Companion:* +https://github.com/hyperpolymath/standards/tree/main/docs/audits[`+hyperpolymath/standards+` +2026-05-26-estate-*-debt audits]. *Combined severity:* `+MEDIUM+`. + +This file records the _raw findings_ — it does not by itself fix the +debt. Each section ends with a '`Recommended next move`' line; closing +the debt is follow-up work. + +=== 1. Proof debt + +No proof-bearing files (`+*.v+`, `+*.lean+`, `+*.agda+`, `+*.idr+`, +`+*.idr2+`, `+*.fst+`, `+*.dfy+`, `+*.tla+`, `+*.ads+`, `+*.adb+`) found +in this repo. + +*Recommended next move:* none. + +=== 2. Licence debt + +[cols=",",options="header",] +|=== +|Field |Value +|LICENSE file |`+LICENSE+` +|SPDX header |`+MPL-2.0+` +|Manifest licence |`+MPL-2.0+` +|Body classifier |`+Palimp-MPL-2.0+` +|Severity |`+ok+` +|=== + +*Recommended next move:* none for licence. + +=== 3. Documentation debt + +[cols=",",options="header",] +|=== +|Field |Value +|README lines |307 +|`+docs/+` files |5 +|`+docs/+` LoC |763 +|CHANGELOG.md |N +|CONTRIBUTING.md |Y +|CODE_OF_CONDUCT.md |N +|SECURITY.md |Y +|Severity |`+MEDIUM+` +|=== + +*Recommended next move:* introduce a `+docs/+` directory. The README at +307 lines has likely grown to do the work of `+docs/+` — split it into a +thin README + `+docs/architecture.md+`, `+docs/usage.md+`, etc. +Heavy-wiki exemplars to copy from: `+affinescript+`, `+boj-server+`, +`+echidna+`, `+hypatia+`. + +Additionally: *CHANGELOG.md is missing.* 65% of estate repos lack one — +adopting a CHANGELOG (or auto-generating via `+git-cliff+`) is a +recommended estate-wide follow-up. + +=== Cross-references + +* Estate proof-debt audit: +`+hyperpolymath/standards/docs/audits/2026-05-26-estate-proof-debt.md+` +* Estate licence-debt audit: +`+hyperpolymath/standards/docs/audits/2026-05-26-estate-licence-debt.md+` +* Estate documentation-debt audit: +`+hyperpolymath/standards/docs/audits/2026-05-26-estate-documentation-debt.md+` + +''''' + +🤖 Generated by Claude Code estate-wide tech-debt scan (2026-05-26). +This file is informational — closing the debt is follow-up work owned by +the maintainer. diff --git a/docs/tech-debt-2026-05-26.md b/docs/tech-debt-2026-05-26.md deleted file mode 100644 index 60dd917..0000000 --- a/docs/tech-debt-2026-05-26.md +++ /dev/null @@ -1,57 +0,0 @@ - - -# Tech-Debt Audit — launch-scaffolder — 2026-05-26 - -**Source:** estate-wide automated scan 2026-05-26. -**Companion:** [`hyperpolymath/standards` 2026-05-26-estate-*-debt audits](https://github.com/hyperpolymath/standards/tree/main/docs/audits). -**Combined severity:** `MEDIUM`. - -This file records the *raw findings* — it does not by itself fix the debt. Each section ends with a 'Recommended next move' line; closing the debt is follow-up work. - -## 1. Proof debt - -No proof-bearing files (`*.v`, `*.lean`, `*.agda`, `*.idr`, `*.idr2`, `*.fst`, `*.dfy`, `*.tla`, `*.ads`, `*.adb`) found in this repo. - -**Recommended next move:** none. - -## 2. Licence debt - -| Field | Value | -|---|---| -| LICENSE file | `LICENSE` | -| SPDX header | `MPL-2.0` | -| Manifest licence | `MPL-2.0` | -| Body classifier | `Palimp-MPL-2.0` | -| Severity | `ok` | - -**Recommended next move:** none for licence. - -## 3. Documentation debt - -| Field | Value | -|---|---| -| README lines | 307 | -| `docs/` files | 5 | -| `docs/` LoC | 763 | -| CHANGELOG.md | N | -| CONTRIBUTING.md | Y | -| CODE_OF_CONDUCT.md | N | -| SECURITY.md | Y | -| Severity | `MEDIUM` | - -**Recommended next move:** introduce a `docs/` directory. The README at 307 lines has likely grown to do the work of `docs/` — split it into a thin README + `docs/architecture.md`, `docs/usage.md`, etc. Heavy-wiki exemplars to copy from: `affinescript`, `boj-server`, `echidna`, `hypatia`. - -Additionally: **CHANGELOG.md is missing.** 65% of estate repos lack one — adopting a CHANGELOG (or auto-generating via `git-cliff`) is a recommended estate-wide follow-up. - -## Cross-references - -- Estate proof-debt audit: `hyperpolymath/standards/docs/audits/2026-05-26-estate-proof-debt.md` -- Estate licence-debt audit: `hyperpolymath/standards/docs/audits/2026-05-26-estate-licence-debt.md` -- Estate documentation-debt audit: `hyperpolymath/standards/docs/audits/2026-05-26-estate-documentation-debt.md` - ---- - -🤖 Generated by Claude Code estate-wide tech-debt scan (2026-05-26). This file is informational — closing the debt is follow-up work owned by the maintainer. diff --git a/examples/README.adoc b/examples/README.adoc new file mode 100644 index 0000000..15ab451 --- /dev/null +++ b/examples/README.adoc @@ -0,0 +1,30 @@ +== `+examples/+` — fixture inputs for `+launch-scaffolder+` + +Every file in this directory is a *test fixture*, not a live per-app +launcher config. Nothing under here is picked up by estate walks +(`+launch-scaffolder realign+`, `+mint --all+`, etc.). + +=== Fixture-vs-live naming rule + +[cols=",,",options="header",] +|=== +|Purpose |File-name suffix |Discovered by estate walks? +|Live per-app config |`+.launcher.a2ml+` |*Yes* +|Fixture / example input |`+.launcher.fixture.a2ml+` |*No* +|=== + +The `+.fixture.+` infix is the single mechanism separating fixtures from +live configs — the discovery code in +`+crates/launcher/src/cmd_realign.rs::is_live_config+` looks for exactly +this suffix and skips anything matching it. Do not rely on directory +names (`+examples/+`, `+tests/+`, …) for isolation; a file named +`+foo.launcher.a2ml+` anywhere under the estate will be treated as a +live config regardless of which directory it lives in. + +=== Rule of thumb for contributors + +* New fixture? Name it `+.launcher.fixture.a2ml+`. +* New live launcher? Name it `+.launcher.a2ml+` and place it in the +target repo, not here. +* Never copy a live config into this directory without renaming it to +the fixture suffix. diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 97b50f9..0000000 --- a/examples/README.md +++ /dev/null @@ -1,32 +0,0 @@ - -# `examples/` — fixture inputs for `launch-scaffolder` - -Every file in this directory is a **test fixture**, not a live per-app -launcher config. Nothing under here is picked up by estate walks -(`launch-scaffolder realign`, `mint --all`, etc.). - -## Fixture-vs-live naming rule - -| Purpose | File-name suffix | Discovered by estate walks? | -|---|---|---| -| Live per-app config | `.launcher.a2ml` | **Yes** | -| Fixture / example input | `.launcher.fixture.a2ml` | **No** | - -The `.fixture.` infix is the single mechanism separating fixtures from -live configs — the discovery code in -`crates/launcher/src/cmd_realign.rs::is_live_config` looks for exactly -this suffix and skips anything matching it. Do not rely on directory -names (`examples/`, `tests/`, …) for isolation; a file named -`foo.launcher.a2ml` anywhere under the estate will be treated as a -live config regardless of which directory it lives in. - -## Rule of thumb for contributors - -- New fixture? Name it `.launcher.fixture.a2ml`. -- New live launcher? Name it `.launcher.a2ml` and place it in the - target repo, not here. -- Never copy a live config into this directory without renaming it to - the fixture suffix.