Author SHA1 Message Date
Tirsvad 56d44989ca Bump the version to 0.3.2
Set VERSION in src/lib/constants.sh to 0.3.2 and the --version check in
tests/test-security.sh to match. This is task 5 of MIL-009, the Git excludes
step. Release v0.3.2 is tagged on Gitea from the merge commit once the pull
request is merged.

Refs #69
Task: MIL-009#5
2026-10-08 14:33:05 +08:00
Tirsvad 642ea775d1 Merge pull request 'Test the Git excludes step (task 4)' (#73) from mil-009-tests into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 4s
Reviewed-on: #73
2026-10-08 08:30:53 +02:00
Tirsvad 7c58c47d8e Test the Git excludes step
Add tests for the step that excludes .claude, .agents and AGENTS.md from git
in the new project: the three paths are ignored and absent from git status
after a run, while framework, .gitmodules and docs/artifact-registry.md stay
visible; no .gitignore is written and nothing is committed; a second run
writes each entry and the comment once and keeps the existing lines, also
when the file has no final newline; nothing is written for a path git
already ignores; a tracked path stays tracked and is named; the step is
skipped without the framework; the dry-run plan lists it.

The existing .env exclusion test also checks its comment line.

Refs #68
Task: MIL-009#4
2026-10-08 14:28:55 +08:00
Tirsvad e23ef3a4b6 Merge pull request 'Describe the files git ignores in the README (task 3)' (#72) from mil-009-readme into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 5s
Reviewed-on: #72
2026-10-08 08:21:54 +02:00
Tirsvad cc157816c7 Describe the files git ignores in the README
Add the "Git excludes" step to the overview, the sample plan, the numbered
run steps and a new section: which paths are excluded and why, that nothing
is committed or changed in a tracked file, that the entries live in
.git/info/exclude and so are not shared with a clone, what happens to a path
git already tracks, and how to track one anyway. Add the tracked-path case to
the error table and the new roles of git.sh and framework.sh to the code
layout.

Closes #67
2026-10-08 14:20:03 +08:00
Tirsvad e9872fa3ed Merge pull request 'Accept MIL-009 and add the Git excludes step (task 2)' (#71) from mil-009-exclude-framework-files into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 4s
Reviewed-on: #71
2026-10-08 08:13:52 +02:00
Tirsvad c379dd4d75 Resolve pending commit links for RC-032 2026-10-08 14:06:46 +08:00
Tirsvad 4058ab4e3e Exclude .claude, .agents and AGENTS.md from git in the new project
After the templates are copied, create-project.sh adds /.claude, /.agents and
/AGENTS.md to .git/info/exclude of the new project as its own step, "Git
excludes". No .gitignore or tracked file changes and nothing is committed.
The step is skipped when the framework steps are skipped, and a path git
already tracks stays tracked and is named in the step.

The .git/info/exclude code of exclude_env_file moves into the shared helper
exclude_from_git (git.sh), which both now use; the .env behavior is unchanged.

Refs #66
Task: MIL-009#2
2026-10-08 14:06:37 +08:00
Tirsvad 1b1b6bec2e Accept MIL-009 after review RC-032
S02 gave the Go in chat on 2026-10-08 and waived the PlantUML render check
(no PlantUML server is configured). Add the review record RC-032, set the
latest Version History rows of MIL-009 and the changed documents to Accepted
and the rows before them to Deprecated, and record the review in the
traceability matrix.
2026-10-08 14:06:36 +08:00
Tirsvad 4287d218ae Merge pull request 'Plan MIL-009: .claude, .agents and AGENTS.md are excluded from git' (#70) from mil-009-exclude-framework-files into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 4s
Reviewed-on: #70
2026-10-08 07:47:12 +02:00
Tirsvad 4f6204c58e Resolve pending commit links for MIL-009 2026-10-08 13:45:56 +08:00
Tirsvad 08cb484498 Plan MIL-009: .claude, .agents and AGENTS.md are excluded from git
Add the phase MIL-009 (proposed 2026-12-21 to 2026-12-23): after the
framework is installed, the new project excludes .claude, .agents and
AGENTS.md through its own .git/info/exclude, so no tracked file changes.

The analysis and design documents agree with it: Business Case objective 5,
US-001.03, UC-001 (postcondition, step 9, extension 9f, a rule), OC-001 (P15
and two exceptions), SD-001, DCD-001 and DCD-002 (excludeFromGit,
trackedPaths), and the Framework Setup and Template definitions in DM-001,
DM-002 and the dictionary.

Refs #65
Refs #66
Refs #67
Refs #68
Refs #69
2026-10-08 13:45:41 +08:00
Tirsvad b1c1fbf178 Merge pull request 'Link the token how-to from the README credentials section' (#64) from howto-link-credentials into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 3s
Reviewed-on: #64
2026-10-08 07:06:45 +02:00
Tirsvad 993722b742 Link the token how-to from the README credentials section
The `.env` (credentials) section now points to howto/create-access-tokens.md
next to the Token permissions reference, so a reader who needs a token finds
the step-by-step guide where the credentials are described.
2026-10-08 13:06:01 +08:00
Tirsvad b0925a51aa Merge pull request 'Add a step-by-step how-to for the Gitea and GitHub tokens' (#63) from howto-access-tokens into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 3s
Reviewed-on: #63
2026-10-08 06:58:07 +02:00
Tirsvad 08d0371da5 Add a step-by-step how-to for the Gitea and GitHub tokens
howto/create-access-tokens.md walks through creating the Gitea access token
and the GitHub classic personal access token that RepoFoundry needs, with nine
illustrations in howto/img/, how to hand the tokens to the script, how to keep
them safe and what the script's token errors mean.

The README's Token permissions section now links to it.
2026-10-08 12:54:46 +08:00
35 changed files with 760 additions and 96 deletions
+52 -5
View File
@@ -8,7 +8,8 @@ RepoFoundry (`src/create-project.sh`) sets up a new project in one run:
- and a **local project** with one credential-free remote, `origin` (Gitea), and the
[SQA-QC-Framework](https://git.tirsystem.com/TirSystem/SQA-QC-Framework)
added as a git submodule, with its skills, git hooks (and optionally the plan
gate) and templates installed.
gate) and templates installed. The files it installs (`.claude`, `.agents` and
`AGENTS.md`) are excluded from git in the new project.
It is a Bash script. It asks for the repository name, description, visibility
and owner (a user or an organization, separately on each host), shows a plan,
@@ -201,7 +202,8 @@ src/create-project.sh --apply # asks only "Create these now (y/n) [n]"
`.env` is ignored by git. The script warns if it is readable by other users or
not ignored by git. See [Token permissions](#token-permissions) for what each
token needs.
token needs, and [How to create the access tokens](howto/create-access-tokens.md)
for the steps, with pictures, to create the Gitea and the GitHub token.
`.env` is optional, and so is each key in it. A credential that is not
provided (the file is missing, the key is absent or its value is empty) is
@@ -258,6 +260,7 @@ Plan:
Framework : add ssh://git@git.example.org:10022/Team/SQA-QC-Framework.git as a submodule
Skills and hooks : install once; plan gate no
Templates : AGENTS.md and docs/artifact-registry.md (you are asked before a file is replaced)
Git excludes : /.claude /.agents /AGENTS.md go into .git/info/exclude (no tracked file changes)
```
Without `--apply` that is all that happens. With `--apply` the script asks
@@ -271,7 +274,10 @@ Without `--apply` that is all that happens. With `--apply` the script asks
repository holds the license commit, that history;
5. the framework as the submodule `framework`;
6. the framework's skills and git hooks, and the plan gate if chosen;
7. `AGENTS.md` and `docs/artifact-registry.md` from the framework's templates.
7. `AGENTS.md` and `docs/artifact-registry.md` from the framework's templates;
8. `/.claude`, `/.agents` and `/AGENTS.md` added to the new project's
`.git/info/exclude`, so git does not list them (see
[The files git ignores](#the-files-git-ignores-in-the-new-project)).
No commit is made in the new project. Work on a branch there: the framework's
hooks refuse commits on `main`.
@@ -297,6 +303,39 @@ it replaces an existing `core.hooksPath`, and before it replaces an existing
replaces a remote that points somewhere else, and git itself refuses to
overwrite a file when the license history is checked out.
### The files git ignores in the new project
The framework installs files that are copies, not the project's own work. After
the templates are copied, the script makes git ignore three paths in the new
project by adding them to its `.git/info/exclude`:
| Path | What it is |
| --- | --- |
| `/.claude` | the skills for the standalone Claude Code CLI (the whole folder) |
| `/.agents` | the skills for Codex CLI and other tools that read `.agents/skills` (the whole folder) |
| `/AGENTS.md` | the project instructions, copied from the framework's template |
- **Nothing is committed and no tracked file changes.** `.gitignore` is not
touched, and `framework`, `.gitmodules` and `docs/artifact-registry.md` stay
visible to git: they are part of the project.
- **The entries belong to this clone.** `.git/info/exclude` is never committed
or pushed, so a fresh clone has neither the entries nor these files. There,
run `git submodule update --init --recursive` and then
`bash framework/scripts/install-skills.sh` to make the skills again.
`AGENTS.md` cannot be made again that way: it is a copy of a template that
you edit, so what you add to it exists only in the clone where you wrote it.
- **The whole `.claude` and `.agents` folders are ignored,** not only their
`skills` folders, so anything else you keep there (settings, agents) is
ignored too.
- **A path git already tracks stays tracked,** because an exclusion does not
apply to a tracked file. The script never removes anything from git; the
summary names the path ("git tracks AGENTS.md, so it is not ignored"). Run
`git rm --cached` on it yourself if you want it ignored.
- **Without SSH to Gitea** the framework steps are skipped, and so is this one.
- **To track one of the paths anyway,** delete its line from
`.git/info/exclude` in the project and `git add` it. Running the script again
in that directory adds the line back.
## SSH access to Gitea
The framework submodule is fetched over SSH on port **10022**
@@ -325,6 +364,9 @@ Both tokens go in `.env` (never in `config.env`, never in a remote URL). The
script sends them only in a request header, through a private temporary file,
and never prints them.
New to this? [How to create the access tokens](howto/create-access-tokens.md)
walks through both, step by step, with pictures.
### GitHub token (`GITHUB_PAT`, only when you choose GitHub)
The same token has two jobs: it creates the repository, and it is the
@@ -406,6 +448,7 @@ step, `2` a usage error.
| A step fails after another succeeded | stops and prints what exists, what failed and how to continue | fix the cause and run the **same command again with `--apply`**: what was created is offered for reuse |
| The mirror is refused (disabled, interval too short) | keeps the repositories and reports it | change `MIRROR_INTERVAL` or ask the Gitea administrator, then run again |
| The framework submodule cannot be fetched | reports the address and how to test SSH | fix your SSH access, run again |
| Git already tracks `.claude`, `.agents` or `AGENTS.md` in the project | leaves it tracked and names it in the summary; the other paths are excluded | `git rm --cached` it if you want it ignored |
| `sync_on_commit` was ignored by Gitea | warns; the mirror syncs on its interval | enable it in the repository settings if needed |
A partial run is reported like this:
@@ -449,6 +492,10 @@ web interface and the project directory by hand.
copy.
- **The framework needs SSH.** Without SSH access to Gitea the framework steps
can only be skipped.
- **The ignored framework files are not shared.** `.claude`, `.agents` and
`AGENTS.md` are excluded in the clone the script made, not in the repository,
so another clone does not have them (see
[The files git ignores](#the-files-git-ignores-in-the-new-project)).
- **Tested on Windows (Git Bash) only so far.** Running the tests on Linux and
macOS is an open follow-up.
@@ -480,9 +527,9 @@ file. The files are loaded from that directory only, by a fixed path.
| `plan.sh` | printing what the script is about to do |
| `repositories.sh` | creating the GitHub and Gitea repositories |
| `mirror.sh` | the Gitea to GitHub push mirror |
| `git.sh` | running git for the new project without prompts or tokens on a command line |
| `git.sh` | running git for the new project without prompts or tokens on a command line, and adding entries to its `.git/info/exclude` |
| `localproject.sh` | the local directory, git repository and remotes |
| `framework.sh` | the framework submodule, skills, hooks and templates |
| `framework.sh` | the framework submodule, skills, hooks and templates, and the git excludes for the files it installs |
| `apply.sh` | confirmations and the apply flow; the only code that changes anything |
| `cli.sh` | usage text and option parsing |
+2 -2
View File
@@ -14,7 +14,7 @@ document of a type. `Primary File` may contain a glob (e.g.
| BC | Business Case | docs/business-case.md | 002 |
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
| PP | Project Plan | docs/project-plan.md | 002 |
| MIL | Milestone / Gateway | docs/milestones/*.md | 009 |
| MIL | Milestone / Gateway | docs/milestones/*.md | 010 |
| US | User Story | docs/user-stories.md | 002 |
| UC | Use Case | docs/uc-*/uc.md | 003 |
| SSD | System Sequence Diagram | docs/uc-*/ssd.md | 003 |
@@ -24,7 +24,7 @@ document of a type. `Primary File` may contain a glob (e.g.
| DCD | Design Class Diagram | docs/dcd.md | 004 |
| DICT | Domain Dictionary (PO and IT terms) | docs/dictionary.md | 002 |
| UCD | Use Case Diagram | docs/use-case-diagram.md | 002 |
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 032 |
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 033 |
| TM | Traceability Matrix | docs/sqa/traceability-matrix.md | 002 |
## Languages
+5 -4
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Objective 11, scope item and criterion 11: the configuration files default to the working folder's, then the checkout's | [0ab5006] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Objective 4: the local project has one remote, origin; no github remote, because a push to origin reaches GitHub through the mirror (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | Objective 4: the local project has one remote, origin; no github remote, because a push to origin reaches GitHub through the mirror (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Objective 5 and scope: the installed `.claude`, `.agents` and `AGENTS.md` are excluded from git in the new project (MIL-009) | [08cb484] |
---
@@ -38,7 +38,7 @@ One repeatable, reviewed procedure gives every new project the same secure basel
2. Create a Gitea repository under a chosen user or organization, empty, or with a license: the one set in `config.env` (`PROJECT_LICENSE`), or AGPL-3.0 when GitHub is chosen, the project is public and none is set.
3. When GitHub was chosen, configure the Gitea repository as a push mirror to GitHub (direction Gitea to GitHub).
4. Create the local project directory with one remote, `origin` (Gitea), containing no credential. There is no `github` remote: a push to `origin` reaches GitHub through the push mirror.
5. Add the SQA-QC-Framework as the `framework` submodule with its own submodules (the `qc` checklists) fetched, install its skills and git hooks, and copy its templates, optionally enabling the plan gate.
5. Add the SQA-QC-Framework as the `framework` submodule with its own submodules (the `qc` checklists) fetched, install its skills and git hooks, and copy its templates, optionally enabling the plan gate. Exclude the installed `.claude`, `.agents` and `AGENTS.md` from git in the new project, through its `.git/info/exclude`, so that no tracked file changes.
6. Never print a token or put one in a URL, a remote or a log, write one to disk only in the new project's own `.env` and only after the Maintainer agrees, and never overwrite existing files or directories without consent.
7. Document installation, configuration, usage, security decisions and error handling in clear English for GitHub readers.
8. Let the Maintainer preset the project details in `config.env`, so that a detail that is set there is not asked again.
@@ -60,6 +60,7 @@ One repeatable, reviewed procedure gives every new project the same secure basel
- Partial-failure reporting with a documented way to continue.
- Starting through a command link: the script finds its own files from the link, the new project lands in the folder it was started in, and `./config.env` and `./.env` there are read before the checkout's (confirmed before the first request).
- Fetching the framework's own submodules (`git submodule update --init --recursive`), so the `qc` checklists are present.
- Excluding `.claude`, `.agents` and `AGENTS.md` from git in the new project through its `.git/info/exclude`.
- Documentation of the SSH prerequisite for the submodule (Gitea SSH on port `10022`).
### Out of Scope
@@ -153,5 +154,5 @@ Proceed — the procedure is small, well bounded and removes a repeated, securit
[SA-001]: ./stakeholder-analysis.md
[UCD-001]: ./use-case-diagram.md
[0ab5006]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/0ab50068bf9e5be82a801af9dbe5b763eeaf7f31
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+11 -7
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | .env is optional (as in UC-001 extension 2b): only config.env is required; a .env found nowhere means the token is asked | [24f1507] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | LocalProjectBuilder.build() no longer takes the GitHub repository; a Local Project has one Remote, origin (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | LocalProjectBuilder.build() no longer takes the GitHub repository; a Local Project has one Remote, origin (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | FrameworkInstaller.excludeFromGit() and InstallResult.trackedPaths: `.claude`, `.agents` and `AGENTS.md` are excluded from git; UC-001 P15 (MIL-009) | [08cb484] |
---
@@ -75,6 +75,7 @@ class LocalProjectBuilder {
}
class FrameworkInstaller {
+install(project : LocalProject, enablePlanGate : Boolean) : InstallResult
-excludeFromGit(project : LocalProject) : String [0..*]
}
class SummaryReport {
+compose(request : ProjectRequest) : Summary
@@ -183,7 +184,9 @@ class Template {
-name : String
-isCopied : Boolean
}
class InstallResult <<dto>>
class InstallResult <<dto>> {
-trackedPaths : String [0..*]
}
class Summary {
-createdItems : String [0..*]
-skippedItems : String [0..*]
@@ -272,7 +275,7 @@ Repository "0..*" --> "1" Visibility
| `GiteaClient` | Git Host (Gitea) | Hides the Gitea API and its token; creates the repository and the push mirror. | none beyond `GitHost` (uses `Configuration`) | `GiteaClient`, `hasLicense`, `createRepository`, `addPushMirror`, `requestSync` |
| `GitHubClient` | Git Host (GitHub) | Hides the GitHub API and its token; creates the empty repository. | none beyond `GitHost` (uses `Configuration`) | `GitHubClient`, `createEmptyRepository` |
| `LocalProjectBuilder` | Local Project, Remote | Creates the project directory, its git repository and its credential-free remotes. | none | `build` |
| `FrameworkInstaller` | Framework, Framework Setup, Template | Adds the framework submodule, installs skills and hooks once, and copies the templates without overwriting. | none | `install` |
| `FrameworkInstaller` | Framework, Framework Setup, Template | Adds the framework submodule, installs skills and hooks once, copies the templates without overwriting, and excludes `.claude`, `.agents` and `AGENTS.md` from git through `.git/info/exclude`. | none | `install`, `excludeFromGit` |
| `SummaryReport` | Summary | Composes the report of what was created, skipped or failed. | none | `compose` |
| `Run` | none (system concept) | Holds the state of one execution. | `isApply` | none |
| `Configuration` | Configuration | Holds the service addresses, the credentials and any preset project details. | `giteaUrl`, `giteaApiUrl`, `githubWebUrl`, `githubApiUrl`, `giteaSshPort`, `mirrorInterval`, `frameworkRepo`, `presetDetails` | none |
@@ -293,7 +296,7 @@ Repository "0..*" --> "1" Visibility
| `HookSetup` | Framework Setup | Records the skills and hooks installed and the plan gate state. | `areSkillsInstalled`, `areHooksInstalled`, `isPlanGateEnabled` | none |
| `EnvFile` | Credentials File | The `.env` of the project: a copy of the credentials it needs. | `address`, `keys` | none |
| `Template` | Template | A framework file copied into the project. | `name`, `isCopied` | none |
| `InstallResult` | none (carries the result of one operation) | Returns the submodule, the hook setup and the templates of `install`. | none | none |
| `InstallResult` | none (carries the result of one operation) | Returns the submodule, the hook setup and the templates of `install`, and the paths git already tracks that could not be excluded. | `trackedPaths` | none |
| `Summary` | Summary | The report returned to the Maintainer; it contains no credential. | `createdItems`, `skippedItems`, `nextSteps` | none |
| `Visibility` | none (enumeration of a Project and Repository attribute) | The two allowed visibilities. | `private`, `public` | none |
@@ -323,6 +326,7 @@ Repository "0..*" --> "1" Visibility
| `GitHubClient.createEmptyRepository(request) : GitHubRepository` | [SD-001] `createEmptyRepository(request)`; P5 |
| `LocalProjectBuilder.build(directory, source, sshPassed) : LocalProject` | [SD-001] `build(directory, giteaRepository, sshPassed)`; P7, P8, P9 |
| `FrameworkInstaller.install(project, enablePlanGate) : InstallResult` | [SD-001] `install(localProject, enablePlanGate)`; P10, P11, P12 |
| `FrameworkInstaller.excludeFromGit(project) : String [0..*]` | [SD-001] `excludeFromGit(localProject)`; P15 |
| `SummaryReport.compose(request) : Summary` | [SD-001] `compose(projectRequest)`; P13 |
## Pattern Annotations
@@ -334,7 +338,7 @@ Repository "0..*" --> "1" Visibility
| Pure Fabrication (GRASP) | `ConfigLoader`, `ToolChecker`, `CredentialCollector`, `EnvFileWriter`, `Preflight`, `LocalProjectBuilder`, `FrameworkInstaller`, `SummaryReport` | No domain concept owns these responsibilities; small units keep cohesion high |
| Creator (GRASP) | `ConfigLoader` creates `Configuration`; `GiteaClient` creates `GiteaRepository` and `PushMirror` | The creating class holds the data needed to build the object |
| Protection from variations (GRASP) | `GiteaClient`, `GitHubClient`, `ProjectRequest` | The optional GitHub path is decided by the controller; the clients do not know it |
| Data Transfer Object (GoF-style) | `InstallResult` | Carries the three results of `install` in one return value |
| Data Transfer Object (GoF-style) | `InstallResult` | Carries the results of `install` (the submodule, the hook setup, the templates and the tracked paths) in one return value |
## Dependency Check
@@ -373,5 +377,5 @@ SOLID check: no class has more than one reason to change (one host API, one kind
[SD-001]: ./uc-001/sd.md
[MIL-005]: ./milestones/mil-005-credentials.md
[DICT-001]: ./dictionary.md
[24f1507]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/24f15070fc73fb06e61865141fe0b825ea9e821e
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+5 -5
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Added Command Link, Checkout and Working Folder (DM-003, UC-002) | [1cd27f7] |
| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S02 | `ConfigFiles` named as a system concept without a PO term | [0ab5006] |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | `ConfigFiles` named as a system concept without a PO term | [0ab5006] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Framework Setup and Template: the folders that hold the skills and the copy of AGENTS.md are ignored by git (MIL-009) | [08cb484] |
---
@@ -36,8 +36,8 @@ Maps each Product Owner (PO) term to its professional IT term. PO language: Engl
| Local Project | en | LocalProject | The project directory on the Maintainer's machine. | DM, UC | OC, SD, DCD |
| Remote | en | Remote | A named link from a local project to a repository. | DM, UC | OC, SD, DCD |
| Framework | en | Submodule | The SQA-QC-Framework added to a local project; the IT term names how it is attached. | DM, UC | OC, SD, DCD |
| Framework Setup | en | HookSetup | The skills and git hooks installed from the framework, with the plan gate on or off. | DM, UC | OC, SD, DCD |
| Template | en | Template | A framework file copied into a project. | DM, UC | OC, SD, DCD |
| Framework Setup | en | HookSetup | The skills and git hooks installed from the framework, with the plan gate on or off; the folders that hold the skills are ignored by git. | DM, UC | OC, SD, DCD |
| Template | en | Template | A framework file copied into a project; the copy of `AGENTS.md` is ignored by git. | DM, UC | OC, SD, DCD |
| Credentials File | en | EnvFile | The file in the local project that holds a copy of the credentials the project needs; owner-only and ignored by git. | DM, UC | OC, SD, DCD |
| Command Link | en | CommandLink | A name on the shell's search path that leads to the script; made by the Maintainer, only followed by the system (no design class). | DM, UC | OC, SD, DCD |
| Checkout | en | Checkout | The folder that holds RepoFoundry and its default `config.env` and `.env`. | DM, UC | OC, SD, DCD |
@@ -68,5 +68,5 @@ Maps each Product Owner (PO) term to its professional IT term. PO language: Engl
[DM-002]: ./domain-model.md
[OC-001]: ./uc-001/oc.md
[DCD-001]: ./uc-001/dcd.md
[1cd27f7]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/1cd27f77ed844773a969210a11de0d8bb98ac98f
[0ab5006]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/0ab50068bf9e5be82a801af9dbe5b763eeaf7f31
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+5 -5
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Default configuration files: --config and --env, else ./config.env and ./.env in the working folder, else the checkout's | [0ab5006] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | A Local Project has one Remote, origin, no longer one or two (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | A Local Project has one Remote, origin, no longer one or two (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Framework Setup and Template: the folders that hold the skills and the copy of AGENTS.md are ignored by git (UC-001, MIL-009) | [08cb484] |
---
@@ -151,8 +151,8 @@ WorkingFolder "1" --> "0..*" LocalProject : is the base of
| Local Project | The project directory on the Maintainer's machine | directory | [UC-001] step 8 "local project" |
| Remote | A named link from a Local Project to a Repository (`origin`) | name, address | [UC-001] step 8 "remote" |
| Framework | The SQA-QC-Framework added to a Local Project | name, address | [UC-001] step 9 "framework submodule" |
| Framework Setup | The skills and git hooks installed from the Framework, with the plan gate on or off | plan gate enabled | [UC-001] step 9 "skills and hooks", "plan gate" |
| Template | A file the Framework provides to copy into a project (`AGENTS.md`, artifact registry) | name | [UC-001] step 9 "templates" |
| Framework Setup | The skills and git hooks installed from the Framework, with the plan gate on or off; the folders that hold the skills (`.claude`, `.agents`) are ignored by git | plan gate enabled | [UC-001] step 9 "skills and hooks", "plan gate", "excludes from git" |
| Template | A file the Framework provides to copy into a project (`AGENTS.md`, artifact registry); the copy of `AGENTS.md` is ignored by git | name | [UC-001] step 9 "templates", "excludes from git" |
| Credentials File | The file in a Local Project that holds a copy of the Access Tokens (and the GitHub account name) the project needs; readable by its owner only and ignored by git | address | [UC-001] step 9 "credentials file" |
| Command Link | A name in a folder on the shell's search path that leads to the script in the Checkout | name, folder | [UC-002] step 1 "command link" |
| Checkout | The folder that holds RepoFoundry: the script, its own files and by default `config.env` and `.env` | path | [UC-002] step 4 "checkout" |
@@ -207,5 +207,5 @@ WorkingFolder "1" --> "0..*" LocalProject : is the base of
[SSD-001]: ./uc-001/ssd.md
[DICT-001]: ./dictionary.md
[DM-001]: ./uc-001/dm.md
[0ab5006]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/0ab50068bf9e5be82a801af9dbe5b763eeaf7f31
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
@@ -0,0 +1,86 @@
# MIL-009 Framework Files Excluded from Git
## Metadata
| Key | Value |
| --- | --- |
| ID | MIL-009 |
| CrossReference | [BC-001], [US-001], [UC-001], [OC-001], [DCD-002] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Initial version | [08cb484] |
---
## Purpose
Decide whether the files the framework installs into a new project, `.claude`, `.agents` and `AGENTS.md`, stay out of that project's git history. The skills in `.claude` and `.agents` are copies made by the framework's installer, and `AGENTS.md` is copied from a framework template. Git should not list them as untracked or offer them to a commit. They are excluded the way the project's `.env` already is: in `.git/info/exclude`, which belongs to the clone, is never committed and changes no tracked file.
## Deliverable
`create-project.sh` that, once the framework's skills and the templates are in place, adds `/.claude`, `/.agents` and `/AGENTS.md` to `.git/info/exclude` of the new project, as its own step ("Git excludes") that shows in the dry-run plan and in the summary. The documents agree with it: Business Case objective 5, US-001.03, UC-001 (postcondition, step 9, extension 9f and a rule), OC-001 (P15 and two exceptions), SD-001, DCD-001 and DCD-002, DM-001 and DM-002, and the dictionary. `README.md` says which files are excluded and why, and how to track one anyway. The tests cover the cases below. The version is raised to 0.3.2, and release `v0.3.2` is tagged on Gitea after the pull request is merged.
The exclusion is not applied when the framework steps are skipped (no SSH to Gitea): nothing was installed. A path that git already tracks stays tracked: the script never runs `git rm`, and the summary names the path, because an exclude entry does not apply to a tracked file. `framework`, `.gitmodules` and `docs/artifact-registry.md` are not excluded; the submodule and the registry are part of the project. The files of this repository (RepoFoundry itself) are unchanged.
## Go / No-Go Criteria
| # | Criterion (objectively checkable) | Go | No-Go |
| --- | --- | --- | --- |
| 1 | After a run with the framework steps, `git check-ignore` reports `.claude`, `.agents` and `AGENTS.md` as ignored in the new project, and `git status --porcelain` lists none of them although they exist | Tests pass | Any of the three listed or not ignored |
| 2 | The entries are written only to `.git/info/exclude`: no `.gitignore` is created or changed, no tracked file changes and nothing is committed | Tests pass | Any other file changed |
| 3 | A second run writes no entry twice, keeps the existing lines of `.git/info/exclude` each on its own line (also when the file has no final newline), and writes nothing for a path that is already ignored | Tests pass | A duplicate, a merged line or a lost line |
| 4 | A path that git already tracks is neither untracked nor changed, and the summary names it as not ignored | Tests pass | A path untracked, or the summary silent |
| 5 | With the framework steps skipped (no SSH to Gitea) nothing is added to `.git/info/exclude` and the step reports `skipped` | Tests pass | An entry written |
| 6 | `framework`, `.gitmodules` and `docs/artifact-registry.md` are not ignored in the new project | Tests pass | Any of them ignored |
| 7 | The dry run lists the "Git excludes" step with the three entries and changes nothing; the summary reports the step in the same words | Tests pass | A change in a dry run, or the step missing |
| 8 | The `.env` exclusion behaves as before: added only after the yes, once, with the same comment line | Tests pass | A changed result |
| 9 | The README, Business Case, UC-001, OC-001, SD-001, DCD-001, DCD-002, DM-001 and DM-002 describe the exclusion and agree with the code; the documents the model does not change say so | Reviewed by S02 | A document that contradicts the code |
| 10 | All acceptance criteria of US-001.03 in [US-001] are met | Verified | Any unmet |
| 11 | `create-project.sh --version` prints `RepoFoundry 0.3.2` | Tests pass | Another version |
## Dependencies
| Depends on | Reason |
| --- | --- |
| [MIL-003] | The framework, skills and templates steps are the ones that install the files |
| [MIL-005] | The `.git/info/exclude` code written for the project's `.env` is reused |
## Traceability
| Business Case objective / KPI / user story | Reference |
| --- | --- |
| User story US-001.03 | [US-001] |
| Objective 5 (the framework, its skills and its templates in the new project) | [BC-001] |
## Ownership
| Role | Stakeholder ID (SA) |
| --- | --- |
| Owner | S01 |
| Approving reviewer | S02 |
## Target Date
2026-12-23 — proposed; the Business Case sets no deadline.
## Tasks
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
| --- | --- | --- | --- | --- |
| 1 | Update the analysis and design documents | Business Case objective 5 and scope, the acceptance criteria of US-001.03, UC-001 (postcondition, step 9, extension 9f for a tracked path, and a rule), OC-001 (postcondition P15 and two exceptions), SD-001 (`excludeFromGit` inside `install`), DCD-001 and DCD-002 (`FrameworkInstaller.excludeFromGit`, `InstallResult.trackedPaths`), and the definitions of Framework Setup and Template in DM-001, DM-002 and the dictionary. | No | |
| 2 | Add the Git excludes step | Move the work of `exclude_env_file` (`src/lib/envfile.sh`) into a helper that adds a list of entries to `.git/info/exclude`: one comment line, a fresh line first, no entry that `git check-ignore` already reports, a check afterwards. `exclude_env_file` keeps its behavior and uses it. Add a function in `src/lib/framework.sh` that excludes `/.claude`, `/.agents` and `/AGENTS.md`, called from `create_all` (`src/lib/apply.sh`) after `copy_templates`, with its own label in `PLAN_STEPS` (`src/lib/constants.sh`) and a line in the dry-run plan (`src/lib/plan.sh`). It is skipped when `is_framework_skipped` and it names any path git tracks. Step 9 of [UC-001] and P12 of [OC-001]. | Yes | [UC-001] |
| 3 | Describe the excluded files in the README | Add the step to the overview and the numbered run steps, and the new line to the sample plan output. Say which paths are excluded and why (they come from the framework and are made again by `bash framework/scripts/install-skills.sh`), that nothing is committed or changed in a tracked file, that the entries live in `.git/info/exclude` and so are not shared with a clone, and how to track one anyway (remove its line from `.git/info/exclude`). | No | |
| 4 | Test the excludes | In the style of `tests/test-credentials.sh`: the three paths ignored and absent from `git status` after a run; a second run adds nothing; a file without a final newline keeps its lines; a tracked `AGENTS.md` stays tracked and is named; framework steps skipped writes nothing; `framework`, `.gitmodules` and `docs/artifact-registry.md` not ignored; the dry run changes nothing; the existing `.env` exclude tests still pass. | No | |
| 5 | Bump the version to 0.3.2 | Set `VERSION` in `src/lib/constants.sh` to 0.3.2 and the `--version` check in `tests/test-security.sh` to match. Release `v0.3.2` is tagged on Gitea from the merge commit once the pull request is merged. | No | |
---
[BC-001]: ../business-case.md
[US-001]: ../user-stories.md
[UC-001]: ../uc-001/uc.md
[OC-001]: ../uc-001/oc.md
[DCD-002]: ../dcd.md
[MIL-003]: ./mil-003-scaffold-and-release.md
[MIL-005]: ./mil-005-credentials.md
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+12 -7
View File
@@ -4,23 +4,23 @@
| Key | Value |
| --- | --- |
| ID | PP-001 |
| CrossReference | [BC-001], [SA-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008], [US-001] |
| CrossReference | [BC-001], [SA-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008], [MIL-009], [US-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Added phase MIL-007 (proposed dates 2026-12-07 to 2026-12-11); MIL-006 deliverable names the public-only AGPL-3.0 default | [1cd27f7] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Added phase MIL-008 (proposed dates 2026-12-14 to 2026-12-18): the local project has origin as its only remote | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | Added phase MIL-008 (proposed dates 2026-12-14 to 2026-12-18): the local project has origin as its only remote | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Added phase MIL-009 (proposed dates 2026-12-21 to 2026-12-23): the framework files .claude, .agents and AGENTS.md are excluded from git | [08cb484] |
---
## Purpose
Schedule the eight phases that deliver RepoFoundry (`create-project.sh` and its documentation) in dependency order. The Business Case sets no deadline, so the dates below are proposals for S01 to confirm.
Schedule the nine phases that deliver RepoFoundry (`create-project.sh` and its documentation) in dependency order. The Business Case sets no deadline, so the dates below are proposals for S01 to confirm.
## Planning Assumptions
- Week 1 starts 2026-10-05; the plan ends by 2026-12-18 (the last three phases are proposed).
- Week 1 starts 2026-10-05; the plan ends by 2026-12-23 (the last four phases are proposed).
- Phase length: two weeks.
- S01 and S02 review each phase through a pull request, as described in [SA-001]. For now one person holds both roles.
- The PO language is English, so no translated copies are kept.
@@ -37,6 +37,7 @@ Schedule the eight phases that deliver RepoFoundry (`create-project.sh` and its
| Project License | [MIL-006] | 2026-11-30 to 2026-12-04 | 2026-12-04 | S01 | US-001.06 | PROJECT_LICENSE in config.env; AGPL-3.0 default only for a public GitHub project | |
| Framework Checklists and Usage | [MIL-007] | 2026-12-07 to 2026-12-11 | 2026-12-11 | S01 | US-001.07 | qc fetched with the framework; global command; README usage | |
| Gitea Is the Only Remote | [MIL-008] | 2026-12-14 to 2026-12-18 | 2026-12-18 | S01 | US-001.03 | The local project has `origin` only; GitHub is reached through the mirror | |
| Framework Files Excluded from Git | [MIL-009] | 2026-12-21 to 2026-12-23 | 2026-12-23 | S01 | US-001.03 | `.claude`, `.agents` and `AGENTS.md` excluded from git in the new project | |
```plantuml
@startgantt
@@ -57,6 +58,8 @@ Project starts 2026-10-05
[Framework Checklists and Usage Go/No-Go] happens 2026-12-11
[Gitea Is the Only Remote] starts 2026-12-14 and ends 2026-12-18
[Gitea Is the Only Remote Go/No-Go] happens 2026-12-18
[Framework Files Excluded from Git] starts 2026-12-21 and ends 2026-12-23
[Framework Files Excluded from Git Go/No-Go] happens 2026-12-23
@endgantt
```
@@ -76,11 +79,12 @@ Project starts 2026-10-05
| Framework's own submodules fetched | [MIL-007] |
| README usage from the target folder and as a global command | [MIL-007] |
| The local project has `origin` as its only remote | [MIL-008] |
| `.claude`, `.agents` and `AGENTS.md` excluded from git in the new project | [MIL-009] |
## Dependencies
```
MIL-001 → MIL-002 → MIL-003 → MIL-004 → MIL-005 → MIL-006 → MIL-007 → MIL-008
MIL-001 → MIL-002 → MIL-003 → MIL-004 → MIL-005 → MIL-006 → MIL-007 → MIL-008 → MIL-009
```
A No-Go moves every later date by the time needed to rework the failed criteria.
@@ -112,11 +116,12 @@ A No-Go moves every later date by the time needed to rework the failed criteria.
[MIL-006]: ./milestones/mil-006-project-license.md
[MIL-007]: ./milestones/mil-007-framework-checklists.md
[MIL-008]: ./milestones/mil-008-gitea-only-remote.md
[MIL-009]: ./milestones/mil-009-exclude-framework-files.md
[US-001]: ./user-stories.md
[UC-001]: ./uc-001/uc.md
[SSD-001]: ./uc-001/ssd.md
[Milestone 43]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/milestone/43
[Milestone 44]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/milestone/44
[Milestone 45]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/milestone/45
[1cd27f7]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/1cd27f77ed844773a969210a11de0d8bb98ac98f
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
@@ -0,0 +1,88 @@
# SQA Review Record: Framework files excluded from git
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-032 |
| CrossReference | [MIL-009], [QC-MIL-001], [MIL-003], [MIL-005], [BC-001], [US-001], [UC-001], [OC-001], [SD-001], [DCD-001], [DCD-002], [DM-001], [DM-002], [DICT-001], [PP-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Initial version: draft for S02's decision | [1b1b6be] |
---
## Artifact Under Review
- Instance reviewed: [MIL-009], and the changes it causes in [BC-001], [US-001], [UC-001], [OC-001], [SD-001], [DCD-001], [DCD-002], [DM-001], [DM-002], [DICT-001], [PP-001] and [TM-001].
- Why: a new project gets `.claude` and `.agents` (the framework's skills, made by `install-skills.sh`) and `AGENTS.md` (copied from a framework template). The request is that git excludes these three in the new project after the framework is installed. The phase adds that step, using the `.git/info/exclude` mechanism that [MIL-005] already uses for the project's `.env`.
- Checklist used: [QC-MIL-001] for [MIL-009]. The other artifacts were changed, not created; their change is checked below.
- Review date: 2026-10-08
## Checklist Results (QC-MIL-001)
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | The script with a new "Git excludes" step that writes `/.claude`, `/.agents` and `/AGENTS.md` to `.git/info/exclude`, shown in the dry-run plan and the summary; the documents that agree with it; the README; the tests; version 0.3.2 and release `v0.3.2`. The cases that are not excluded (framework steps skipped, a tracked path, `framework`, `.gitmodules`, the registry) are stated. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Eleven criteria, each with an objective Go and No-Go: ignored and absent from `git status`, only `.git/info/exclude` written, a second run and a file without a final newline, a tracked path, framework steps skipped, paths that must stay visible, dry run and summary, the `.env` exclusion unchanged, the documents in agreement, the acceptance criteria of US-001.03, and the version. Criteria 9 and 10 are "Reviewed by S02" and "Verified", as in [MIL-007] and [MIL-008]. |
| 3 | Dependencies on other milestones are explicitly mapped | Pass | [MIL-003] (the framework, skills and templates steps install the files) and [MIL-005] (the `.git/info/exclude` code is reused), each with its reason. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | Objective 5 of [BC-001], which this phase extends, and US-001.03. No success criterion fits, so none is cited. |
| 5 | Milestone owner and approving reviewer are identified | Pass | Owner S01, approving reviewer S02. |
| 6 | Milestone has a defined target date consistent with project constraints | Pass | 2026-12-23, proposed in [PP-001]; the window 2026-12-21 (Monday) to 2026-12-23 (Wednesday) follows [MIL-008], which ends Friday 2026-12-18. The Business Case sets no deadline. |
## Change checks on the other artifacts
| Artifact | Change | Status | Evidence/Notes |
| --- | --- | --- | --- |
| [BC-001] | Objective 5 and one in-scope item name the exclusion | Pass | The out-of-scope list (no first commit) is consistent: nothing is committed. |
| [US-001] | US-001.03 acceptance criterion; [MIL-009] in the CrossReference and the Traces row | Pass | One given/when/then. No new story: the behavior belongs to the local-project story, so the count of seven stories is unchanged. |
| [UC-001] | Postcondition, step 9, extension 9f, and a rule for step 9 | Pass | The rule fixes the mechanism (`.git/info/exclude`, never `.gitignore`), the whole folders, the paths that stay visible, and a tracked path. |
| [OC-001] | P15 and two exceptions | Pass | P15 is appended, so P1 to P14 and the references to them in [SD-001], [DCD-001] and [DCD-002] still hold. |
| [SD-001] | Self-message `excludeFromGit(localProject)` in `install`, return `installResult (trackedPaths)`, coverage row P15 | Pass | The pattern is the one already used for `requestSync`. Diagram not rendered (see action items). |
| [DCD-001], [DCD-002] | `FrameworkInstaller.excludeFromGit`, `InstallResult.trackedPaths`, the class table, the method traceability and the DTO sentence | Pass | Names and signature match [SD-001]. Diagram not rendered (see action items). |
| [DM-001], [DM-002], [DICT-001] | Definitions of Framework Setup and Template | Pass | No new concept or attribute, as for Credentials File ("ignored by git"). The use-case model, the project model and the dictionary say the same. |
| [PP-001] | Phase [MIL-009], its window, the Gantt, the scope coverage, the dependency chain, "nine phases" and the end date | Pass | Proposed dates 2026-12-21 to 2026-12-23. |
| [TM-001] | Row for [MIL-009] and this record; Last Reviewed updated for each changed artifact | Pass | See the matrix. |
Mechanical checks run for this review: every link definition of the changed files points at an existing file and none is unused; the Gantt dates fall on the weekdays named above; [MIL-009] has 11 criteria and 5 tasks; `sync-project.sh --milestone MIL-009` parses it (milestone 87, issues #65 to #69 created on Gitea); the new names (`excludeFromGit`, `trackedPaths`, P15) are the same in every document that uses them.
Not covered by this review: the code, the README and the tests (tasks 2 to 5 of [MIL-009]) do not exist yet. Criteria 1 to 8, 10 and 11 of [MIL-009] are checked at its gate, not here; criterion 9 is checked then too.
## Consequences S02 accepts with a Go
- The whole folders `.claude` and `.agents` are excluded, as requested, not only their `skills` folders. Anything else a project keeps there (settings, agents) is not tracked either.
- The entries live in `.git/info/exclude`, which belongs to the clone. A fresh clone has neither the entries nor the files. The skills can be made again with `install-skills.sh`; `AGENTS.md` cannot, because it is a copy of a template that the project edits.
## Overall Verdict
Go — every criterion of the checklist passes and the changed documents agree with each other. One check was not done: the PlantUML diagrams of [SD-001], [DCD-001] and [DCD-002] were not rendered, because no PlantUML server is configured and the framework does not choose one. Drafted by Claude Code for S02; the author and reviewer are the same person for now, as in [RC-031]. S02 gave the Go in chat on 2026-10-08, accepted the consequences listed above, and waived the diagram check; the Version History rows of [MIL-009] and the changed documents were set to `Accepted` and the rows before them to `Deprecated`.
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None. The diagram check was waived by S02 in chat on 2026-10-08; the three diagrams stay unrendered, and a syntax error found later is fixed in the document concerned | - | - |
---
[MIL-009]: ../../milestones/mil-009-exclude-framework-files.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[MIL-003]: ../../milestones/mil-003-scaffold-and-release.md
[MIL-005]: ../../milestones/mil-005-credentials.md
[MIL-007]: ../../milestones/mil-007-framework-checklists.md
[MIL-008]: ../../milestones/mil-008-gitea-only-remote.md
[BC-001]: ../../business-case.md
[US-001]: ../../user-stories.md
[UC-001]: ../../uc-001/uc.md
[OC-001]: ../../uc-001/oc.md
[SD-001]: ../../uc-001/sd.md
[DCD-001]: ../../uc-001/dcd.md
[DCD-002]: ../../dcd.md
[DM-001]: ../../uc-001/dm.md
[DM-002]: ../../domain-model.md
[DICT-001]: ../../dictionary.md
[PP-001]: ../../project-plan.md
[TM-001]: ../traceability-matrix.md
[RC-031]: ./rc-031-gitea-only-remote.md
[1b1b6be]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/1b1b6bec2effe5f566479ef94fcc5bc73a9fd609
+18 -15
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Added review RC-030 (.env optional) | [24f1507] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Added MIL-008 with its review RC-031 (Gitea is the only remote) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | Added MIL-009 (framework files excluded from git); not yet reviewed | [08cb484] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Added review RC-032 (MIL-009: framework files excluded from git), Go | [1b1b6be] |
---
@@ -24,9 +24,9 @@ updated whenever an artifact instance is created or reviewed.
| Artifact Instance | Type | Upstream (Backward Link) | Downstream (Forward Link) | Last Reviewed (RC-ID) |
| --- | --- | --- | --- | --- |
| [BC-001] | BC | - | [SA-001], [PP-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008], [US-001], [UCD-001] | [RC-010], [RC-018], [RC-020], [RC-022], [RC-029], [RC-031] |
| [BC-001] | BC | - | [SA-001], [PP-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008], [MIL-009], [US-001], [UCD-001] | [RC-010], [RC-018], [RC-020], [RC-022], [RC-029], [RC-031], [RC-032] |
| [SA-001] | SA | [BC-001] | [UCD-001], [UC-001], [DICT-001] | [RC-013] |
| [PP-001] | PP | [BC-001], [SA-001] | [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008] | [RC-012], [RC-018], [RC-020], [RC-022], [RC-031] |
| [PP-001] | PP | [BC-001], [SA-001] | [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008], [MIL-009] | [RC-012], [RC-018], [RC-020], [RC-022], [RC-031], [RC-032] |
| [MIL-001] | MIL | [BC-001], [PP-001] | [US-001] | [RC-011], [RC-016] |
| [MIL-002] | MIL | [BC-001], [PP-001] | [US-001] | [RC-014], [RC-017] |
| [MIL-003] | MIL | [BC-001], [PP-001] | [US-001] | [RC-015], [RC-017], [RC-031] |
@@ -35,9 +35,10 @@ updated whenever an artifact instance is created or reviewed.
| [MIL-006] | MIL | [BC-001], [PP-001] | [US-001] | [RC-022] |
| [MIL-007] | MIL | [BC-001], [PP-001] | [US-001], [UC-002] | [RC-022], [RC-029], [RC-030] |
| [MIL-008] | MIL | [BC-001], [PP-001] | [US-001] | [RC-031] |
| [MIL-009] | MIL | [BC-001], [PP-001] | [US-001] | [RC-032] |
| [UCD-001] | UCD | [BC-001], [SA-001] | [US-001], [UC-001], [UC-002] | [RC-009], [RC-023] |
| [US-001] | US | [BC-001], [UCD-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008] | [UC-001] | [RC-001], [RC-020], [RC-022], [RC-029], [RC-030], [RC-031] |
| [UC-001] | UC | [UCD-001], [US-001], [SA-001] | [SSD-001], [DM-001] | [RC-002], [RC-020], [RC-023], [RC-031] |
| [US-001] | US | [BC-001], [UCD-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008], [MIL-009] | [UC-001] | [RC-001], [RC-020], [RC-022], [RC-029], [RC-030], [RC-031], [RC-032] |
| [UC-001] | UC | [UCD-001], [US-001], [SA-001] | [SSD-001], [DM-001] | [RC-002], [RC-020], [RC-023], [RC-031], [RC-032] |
| [UC-002] | UC | [UCD-001], [US-001], [SA-001], [BC-001] | [SSD-002], [DM-003] | [RC-023], [RC-029], [RC-030] |
| [SSD-002] | SSD | [UC-002], [DM-003] | [OC-002] | [RC-024], [RC-029] |
| [DM-003] | DM | [UC-002], [UCD-001], [SSD-002], [DICT-001], [DM-001] | [OC-002], [DCD-003] | [RC-025], [RC-029] |
@@ -45,13 +46,13 @@ updated whenever an artifact instance is created or reviewed.
| [SD-002] | SD | [OC-002], [DCD-003] | [DCD-003] | [RC-027], [RC-029] |
| [DCD-003] | DCD | [DM-003], [SD-002], [DICT-001], [UC-002], [DCD-001] | [DCD-002] | [RC-028], [RC-029], [RC-030] |
| [SSD-001] | SSD | [UC-001] | [OC-001] | [RC-003], [RC-020] |
| [DM-001] | DM | [UC-001], [SSD-001] | [DM-002], [DICT-001], [OC-001], [DCD-001] | [RC-004], [RC-020], [RC-031] |
| [DM-002] | DM | [DM-001] | [DICT-001], [DCD-001], [DCD-002] | [RC-005], [RC-020], [RC-025], [RC-029], [RC-031] |
| [DICT-001] | DICT | [BC-001], [SA-001], [DM-001], [DM-002] | [OC-001], [SD-001] | [RC-008], [RC-020], [RC-025], [RC-029] |
| [OC-001] | OC | [SSD-001], [DM-001] | [SD-001] | [RC-006], [RC-020], [RC-026], [RC-031] |
| [SD-001] | SD | [OC-001] | [DCD-001] | [RC-007], [RC-020], [RC-021], [RC-031] |
| [DCD-001] | DCD | [UC-001], [DM-001], [DM-002], [OC-001], [SD-001], [DICT-001] | [DCD-002] | [RC-021], [RC-031] |
| [DCD-002] | DCD | [DCD-001], [DCD-003], [DM-002], [DICT-001] | - | [RC-021], [RC-028], [RC-029], [RC-030], [RC-031] |
| [DM-001] | DM | [UC-001], [SSD-001] | [DM-002], [DICT-001], [OC-001], [DCD-001] | [RC-004], [RC-020], [RC-031], [RC-032] |
| [DM-002] | DM | [DM-001] | [DICT-001], [DCD-001], [DCD-002] | [RC-005], [RC-020], [RC-025], [RC-029], [RC-031], [RC-032] |
| [DICT-001] | DICT | [BC-001], [SA-001], [DM-001], [DM-002] | [OC-001], [SD-001] | [RC-008], [RC-020], [RC-025], [RC-029], [RC-032] |
| [OC-001] | OC | [SSD-001], [DM-001] | [SD-001] | [RC-006], [RC-020], [RC-026], [RC-031], [RC-032] |
| [SD-001] | SD | [OC-001] | [DCD-001] | [RC-007], [RC-020], [RC-021], [RC-031], [RC-032] |
| [DCD-001] | DCD | [UC-001], [DM-001], [DM-002], [OC-001], [SD-001], [DICT-001] | [DCD-002] | [RC-021], [RC-031], [RC-032] |
| [DCD-002] | DCD | [DCD-001], [DCD-003], [DM-002], [DICT-001] | - | [RC-021], [RC-028], [RC-029], [RC-030], [RC-031], [RC-032] |
## Coverage Notes
@@ -71,6 +72,7 @@ updated whenever an artifact instance is created or reviewed.
[MIL-006]: ../milestones/mil-006-project-license.md
[MIL-007]: ../milestones/mil-007-framework-checklists.md
[MIL-008]: ../milestones/mil-008-gitea-only-remote.md
[MIL-009]: ../milestones/mil-009-exclude-framework-files.md
[RC-018]: ./reviews/rc-018-mil-004.md
[RC-019]: ./reviews/rc-019-mil-004-code.md
[RC-020]: ./reviews/rc-020-mil-005.md
@@ -85,6 +87,7 @@ updated whenever an artifact instance is created or reviewed.
[RC-029]: ./reviews/rc-029-default-config-files.md
[RC-030]: ./reviews/rc-030-env-optional.md
[RC-031]: ./reviews/rc-031-gitea-only-remote.md
[RC-032]: ./reviews/rc-032-exclude-framework-files.md
[DCD-001]: ../uc-001/dcd.md
[DCD-002]: ../dcd.md
[UCD-001]: ../use-case-diagram.md
@@ -119,5 +122,5 @@ updated whenever an artifact instance is created or reviewed.
[RC-015]: ./reviews/rc-015-mil-003.md
[RC-016]: ./reviews/rc-016-create-project-sh.md
[RC-017]: ./reviews/rc-017-e2e-security-review.md
[24f1507]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/24f15070fc73fb06e61865141fe0b825ea9e821e
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
[1b1b6be]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/1b1b6bec2effe5f566479ef94fcc5bc73a9fd609
+11 -7
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Note that DCD-003 and DCD-002 supersede the signature of startProjectCreation | [0b0a3b4] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | LocalProjectBuilder.build() no longer takes the GitHub repository; a Local Project has one Remote, origin (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | LocalProjectBuilder.build() no longer takes the GitHub repository; a Local Project has one Remote, origin (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | FrameworkInstaller.excludeFromGit() and InstallResult.trackedPaths: `.claude`, `.agents` and `AGENTS.md` are excluded from git; P15 (MIL-009) | [08cb484] |
---
@@ -79,6 +79,7 @@ class LocalProjectBuilder {
}
class FrameworkInstaller {
+install(project : LocalProject, enablePlanGate : Boolean) : InstallResult
-excludeFromGit(project : LocalProject) : String [0..*]
}
class SummaryReport {
+compose(request : ProjectRequest) : Summary
@@ -167,7 +168,9 @@ class Template {
-name : String
-isCopied : Boolean
}
class InstallResult <<dto>>
class InstallResult <<dto>> {
-trackedPaths : String [0..*]
}
class Summary {
-createdItems : String [0..*]
-skippedItems : String [0..*]
@@ -245,7 +248,7 @@ Repository "0..*" --> "1" Visibility
| `GiteaClient` | Git Host (Gitea) | Hides the Gitea API and its token; creates the repository and the push mirror. | none beyond `GitHost` (uses `Configuration`) | `GiteaClient`, `hasLicense`, `createRepository`, `addPushMirror`, `requestSync` |
| `GitHubClient` | Git Host (GitHub) | Hides the GitHub API and its token; creates the empty repository. | none beyond `GitHost` (uses `Configuration`) | `GitHubClient`, `createEmptyRepository` |
| `LocalProjectBuilder` | Local Project, Remote | Creates the project directory, its git repository and its credential-free remotes. | none | `build` |
| `FrameworkInstaller` | Framework, Framework Setup, Template | Adds the framework submodule, installs skills and hooks once, and copies the templates without overwriting. | none | `install` |
| `FrameworkInstaller` | Framework, Framework Setup, Template | Adds the framework submodule, installs skills and hooks once, copies the templates without overwriting, and excludes `.claude`, `.agents` and `AGENTS.md` from git through `.git/info/exclude`. | none | `install`, `excludeFromGit` |
| `SummaryReport` | Summary | Composes the report of what was created, skipped or failed. | none | `compose` |
| `Run` | none (system concept) | Holds the state of one execution. | `isApply` | none |
| `Configuration` | Configuration | Holds the service addresses, the credentials and any preset project details. | `giteaUrl`, `giteaApiUrl`, `githubWebUrl`, `githubApiUrl`, `giteaSshPort`, `mirrorInterval`, `frameworkRepo`, `presetDetails` | none |
@@ -266,7 +269,7 @@ Repository "0..*" --> "1" Visibility
| `HookSetup` | Framework Setup | Records the skills and hooks installed and the plan gate state. | `areSkillsInstalled`, `areHooksInstalled`, `isPlanGateEnabled` | none |
| `EnvFile` | Credentials File | The `.env` of the project: a copy of the credentials it needs. | `address`, `keys` | none |
| `Template` | Template | A framework file copied into the project. | `name`, `isCopied` | none |
| `InstallResult` | none (carries the result of one operation) | Returns the submodule, the hook setup and the templates of `install`. | none | none |
| `InstallResult` | none (carries the result of one operation) | Returns the submodule, the hook setup and the templates of `install`, and the paths git already tracks that could not be excluded. | `trackedPaths` | none |
| `Summary` | Summary | The report returned to the Maintainer; it contains no credential. | `createdItems`, `skippedItems`, `nextSteps` | none |
| `Visibility` | none (enumeration of a Project and Repository attribute) | The two allowed visibilities. | `private`, `public` | none |
@@ -293,6 +296,7 @@ Repository "0..*" --> "1" Visibility
| `GitHubClient.createEmptyRepository(request) : GitHubRepository` | [SD-001] `createEmptyRepository(request)`; P5 |
| `LocalProjectBuilder.build(directory, source, sshPassed) : LocalProject` | [SD-001] `build(directory, giteaRepository, sshPassed)`; P7, P8, P9 |
| `FrameworkInstaller.install(project, enablePlanGate) : InstallResult` | [SD-001] `install(localProject, enablePlanGate)`; P10, P11, P12 |
| `FrameworkInstaller.excludeFromGit(project) : String [0..*]` | [SD-001] `excludeFromGit(localProject)`; P15 |
| `SummaryReport.compose(request) : Summary` | [SD-001] `compose(projectRequest)`; P13 |
## Pattern Annotations
@@ -304,7 +308,7 @@ Repository "0..*" --> "1" Visibility
| Pure Fabrication (GRASP) | `ConfigLoader`, `ToolChecker`, `CredentialCollector`, `EnvFileWriter`, `Preflight`, `LocalProjectBuilder`, `FrameworkInstaller`, `SummaryReport` | No domain concept owns these responsibilities; small units keep cohesion high |
| Creator (GRASP) | `ConfigLoader` creates `Configuration`; `GiteaClient` creates `GiteaRepository` and `PushMirror` | The creating class holds the data needed to build the object |
| Protection from variations (GRASP) | `GiteaClient`, `GitHubClient`, `ProjectRequest` | The optional GitHub path is decided by the controller; the clients do not know it |
| Data Transfer Object (GoF-style) | `InstallResult` | Carries the three results of `install` in one return value |
| Data Transfer Object (GoF-style) | `InstallResult` | Carries the results of `install` (the submodule, the hook setup, the templates and the tracked paths) in one return value |
## Dependency Check
@@ -341,5 +345,5 @@ SOLID check: no class has more than one reason to change (one host API, one kind
[DCD-002]: ../dcd.md
[DCD-003]: ../uc-002/dcd.md
[UC-002]: ../uc-002/uc.md
[0b0a3b4]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/0b0a3b419a1157b23bddd2f8957a08adaf6974a6
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+5 -5
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | License: the AGPL-3.0 default needs GitHub and a public project | [1cd27f7] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | A Local Project has one Remote, origin, no longer one or two (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | A Local Project has one Remote, origin, no longer one or two (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Framework Setup and Template: the folders that hold the skills and the copy of AGENTS.md are ignored by git (MIL-009) | [08cb484] |
---
@@ -135,8 +135,8 @@ Summary "1" --> "1" Project : reports on
| Local Project | The project directory on the Maintainer's machine | directory | [UC-001] step 8 "local project" |
| Remote | A named link from a Local Project to a Repository (`origin`) | name, address | [UC-001] step 8 "remote" |
| Framework | The SQA-QC-Framework added to a Local Project | name, address | [UC-001] step 9 "framework submodule" |
| Framework Setup | The skills and git hooks installed from the Framework, with the plan gate on or off | plan gate enabled | [UC-001] step 9 "skills and hooks", "plan gate" |
| Template | A file the Framework provides to copy into a project (`AGENTS.md`, artifact registry) | name | [UC-001] step 9 "templates" |
| Framework Setup | The skills and git hooks installed from the Framework, with the plan gate on or off; the folders that hold the skills (`.claude`, `.agents`) are ignored by git | plan gate enabled | [UC-001] step 9 "skills and hooks", "plan gate", "excludes from git" |
| Template | A file the Framework provides to copy into a project (`AGENTS.md`, artifact registry); the copy of `AGENTS.md` is ignored by git | name | [UC-001] step 9 "templates", "excludes from git" |
| Credentials File | The file in a Local Project that holds a copy of the Access Tokens (and the GitHub account name) the project needs; readable by its owner only and ignored by git | address | [UC-001] step 9 "credentials file" |
| Summary | The report of what was created, skipped or failed and how to continue | created items, skipped items, next steps | [UC-001] step 10 "summary" |
@@ -180,5 +180,5 @@ Summary "1" --> "1" Project : reports on
[SSD-001]: ./ssd.md
[DICT-001]: ../dictionary.md
[DM-002]: ../domain-model.md
[1cd27f7]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/1cd27f77ed844773a969210a11de0d8bb98ac98f
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+6 -3
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Note that OC-002 and DCD-002 supersede the signature of startProjectCreation | [0b0a3b4] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | P9: no other remote is associated with the local project (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | P9: no other remote is associated with the local project (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | P15 and two exceptions: `.claude`, `.agents` and `AGENTS.md` are excluded from git; a tracked path is reported (MIL-009) | [08cb484] |
---
@@ -77,6 +77,7 @@ Concepts below use the IT terms of [DICT-001] for the PO concepts of [DM-001]. `
- P12. `AGENTS.md` and `docs/artifact-registry.md` exist in the `LocalProject`, each either newly copied from the framework templates or left as it was because the Maintainer declined to replace it.
- P13. A `Summary` instance was created listing every created item, every skipped item and the next step for anything that failed, and is returned. It contains no credential.
- P14. If `writeEnvFile`, an `EnvFile` named `.env` was associated with the `LocalProject`, holding only the `Credential`s the project needs (the Gitea token, and the GitHub token and account name when `githubOwner` is present). It is readable by its owner only and excluded from git without a change to any tracked file, and no `Credential` is shown in any output. If `writeEnvFile` is false, no `EnvFile` was created. An existing `.env` is left as it was unless the Maintainer agreed to replace it.
- P15. If the `Submodule` and the `HookSetup` were created (P10, P11), the paths `.claude`, `.agents` and `AGENTS.md` of the `LocalProject` are excluded from git: each has an entry in the `.git/info/exclude` of the `LocalProject`, whether or not the path exists yet and whether or not the Maintainer declined replacing `AGENTS.md` (P12). No tracked file was changed and no commit was made. A path that git already tracks stays tracked, and the `Summary` lists it as not excluded. `framework`, `.gitmodules` and `docs/artifact-registry.md` are not excluded.
**Exceptions**
@@ -91,6 +92,8 @@ Concepts below use the IT terms of [DICT-001] for the PO concepts of [DM-001]. `
| A different `core.hooksPath` exists and the Maintainer declines replacing it (P11) | Hooks are not installed and this is listed in the `Summary` |
| The framework's own submodules cannot be fetched (P10) | The `Summary` lists the `framework` `Submodule` as added, its own submodules as failed, and the command `git submodule update --init --recursive` to run by hand |
| SSH to port 10022 fails and the `Submodule` cannot be added (P10) | The `Summary` lists the repositories as created, the submodule as failed, and the SSH prerequisite |
| SSH to port 10022 fails and the Maintainer goes on without the framework (P10, P11, P12, P15) | No `Submodule`, `HookSetup` or template is created and no path is excluded; the `Summary` lists each of these steps as skipped |
| `.claude`, `.agents` or `AGENTS.md` is already tracked by git (P15) | The path stays tracked and is listed in the `Summary` as not excluded; the other paths are excluded |
---
@@ -101,5 +104,5 @@ Concepts below use the IT terms of [DICT-001] for the PO concepts of [DM-001]. `
[OC-002]: ../uc-002/oc.md
[DCD-002]: ../dcd.md
[UC-002]: ../uc-002/uc.md
[0b0a3b4]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/0b0a3b419a1157b23bddd2f8957a08adaf6974a6
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+6 -4
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Note that SD-002 and DCD-002 supersede the signature of startProjectCreation | [0b0a3b4] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | build() no longer receives the GitHub repository and returns a local project with the remote origin; P9 (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | build() no longer receives the GitHub repository and returns a local project with the remote origin; P9 (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | install() excludes the installed files from git with excludeFromGit() and returns the tracked paths; P15 (MIL-009) | [08cb484] |
---
@@ -154,7 +154,8 @@ deactivate LB
create FI
PC -> FI : install(localProject, enablePlanGate)
activate FI
FI --> PC : installResult
FI -> FI : excludeFromGit(localProject)
FI --> PC : installResult (trackedPaths)
deactivate FI
opt writeEnvFile
@@ -209,6 +210,7 @@ destroy SR
| P12 AGENTS.md and registry copied or kept | `install(...)` returning `templates` |
| P13 Summary created and returned | `compose(request)` and the final return |
| P14 EnvFile created with the needed credentials, owner-only, ignored by git, or none when declined | `write(localProject, configuration, githubOwner present)` inside `opt writeEnvFile` |
| P15 `.claude`, `.agents` and `AGENTS.md` excluded from git; a tracked path reported | `excludeFromGit(localProject)` inside `install(...)`, and `installResult (trackedPaths)` for the `Summary` |
### Responsibility Check
@@ -221,5 +223,5 @@ destroy SR
[SD-002]: ../uc-002/sd.md
[DCD-002]: ../dcd.md
[UC-002]: ../uc-002/uc.md
[0b0a3b4]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/0b0a3b419a1157b23bddd2f8957a08adaf6974a6
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+8 -4
View File
@@ -9,8 +9,8 @@
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | License rule: the AGPL-3.0 default needs GitHub and a public project (step 6, rule 6); step 9 and extension 9e fetch the framework's own submodules | [1cd27f7] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Postcondition, step 8 and its rule: origin is the only remote; no github remote (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | Postcondition, step 8 and its rule: origin is the only remote; no github remote (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | Postcondition, step 9, extension 9f and a rule: `.claude`, `.agents` and `AGENTS.md` are excluded from git (MIL-009) | [08cb484] |
---
@@ -34,6 +34,7 @@
- A repository exists on Gitea under the chosen owner. It is empty, or it holds the license file that applies: the license set in `config.env`, or AGPL-3.0 when the Maintainer chose GitHub, the project is public and no license is set.
- When the Maintainer chose to create a GitHub repository, an empty repository exists on GitHub under the chosen owner, the Gitea repository is a push mirror to it, and the license file, if any, reaches GitHub through the mirror.
- A local project directory exists with one credential-free remote, `origin` (Gitea), the `framework` submodule, installed skills and hooks, and the copied templates.
- Git ignores `.claude`, `.agents` and `AGENTS.md` in the local project, through the project's own `.git/info/exclude`; no tracked file is changed.
- When the Maintainer agreed, the local project has a `.env` that holds only the credentials the project needs, is readable by its owner only and is ignored by git.
- The Maintainer has a summary of what was created.
@@ -47,7 +48,7 @@
6. The system creates the Gitea repository. If a license applies, the repository is created with its license file and so is not empty; otherwise it is empty. The license that applies is the one set in `config.env` (`PROJECT_LICENSE`; `none` means no license); when none is set it is AGPL-3.0 if the Maintainer chose GitHub and the project is public, and none otherwise. The license is never asked.
7. Optional: if GitHub was chosen, the system configures the Gitea repository as a push mirror to GitHub and verifies it. A license file in the Gitea repository is pushed to GitHub by the mirror.
8. The system creates the local project with the `origin` remote. It adds no `github` remote: a push to `origin` reaches GitHub through the mirror of step 7.
9. The system adds the framework submodule and fetches its own submodules (the `qc` checklists), installs its skills and hooks (and the plan gate if chosen) and copies the templates. If the Maintainer agrees, it also creates the project's own `.env` with the credentials the project needs.
9. The system adds the framework submodule and fetches its own submodules (the `qc` checklists), installs its skills and hooks (and the plan gate if chosen) and copies the templates. It then excludes `.claude`, `.agents` and `AGENTS.md` from git. If the Maintainer agrees, it also creates the project's own `.env` with the credentials the project needs.
10. The system reports a summary of what was created.
### Extensions (Alternative / Exception Flows)
@@ -78,6 +79,8 @@
1. The system asks before replacing it; on no, it keeps it and reports it.
- 9e. The framework's own submodules cannot be fetched:
1. The system stops the step, reports what exists and names the command to run by hand, `git submodule update --init --recursive`, without showing a credential.
- 9f. Git already tracks one of `.claude`, `.agents` and `AGENTS.md`:
1. The system leaves it tracked, because an exclusion does not apply to a tracked file, and names the path in the summary as not ignored.
### Special Requirements / Business Rules
@@ -93,6 +96,7 @@
| 7 | The mirror direction is Gitea to GitHub; the GitHub repository stays empty and receives its content from the mirror |
| 8 | `origin` is the only remote, with or without GitHub, and a remote already in the directory, such as a `github` remote made by an earlier version, is never removed or replaced. `origin` uses HTTPS derived from `GITEA_URL`, or SSH when the SSH test in step 4 passed; when the Gitea repository is not empty (GitHub chosen) the local project is created by fetching it, not by an unrelated `git init` history |
| 8, 9 | Nothing is overwritten or deleted without consent, and no commit is made |
| 9 | `.claude`, `.agents` and `AGENTS.md` are excluded from git only when the framework steps ran, whether or not the files were newly made, through the project's `.git/info/exclude` (never `.gitignore`, so no tracked file changes and nothing is shared with a clone), once, anchored to the project root, and the whole folders `.claude` and `.agents` are excluded, not only their `skills` folders. `framework`, `.gitmodules` and `docs/artifact-registry.md` are not excluded. A path git already tracks is never untracked or changed |
### Open Issues
@@ -104,5 +108,5 @@
[US-001]: ../user-stories.md
[SA-001]: ../stakeholder-analysis.md
[DM-001]: ./dm.md
[1cd27f7]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/1cd27f77ed844773a969210a11de0d8bb98ac98f
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+7 -5
View File
@@ -4,13 +4,13 @@
| Key | Value |
| --- | --- |
| ID | US-001 |
| CrossReference | [BC-001], [UCD-001], [UC-002], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008] |
| CrossReference | [BC-001], [UCD-001], [UC-002], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [MIL-005], [MIL-006], [MIL-007], [MIL-008], [MIL-009] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | US-002: only config.env is required; a .env found nowhere means the token is asked | [24f1507] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | US-001.03: one remote, origin; no github remote (MIL-008) | [039a28c] |
| 2026-10-08 | Deprecated | Jens Tirsvad Nielsen | S02 | US-001.03: one remote, origin; no github remote (MIL-008) | [039a28c] |
| 2026-10-08 | Accepted | Jens Tirsvad Nielsen | S02 | US-001.03: `.claude`, `.agents` and `AGENTS.md` are excluded from git; MIL-009 added to CrossReference | [08cb484] |
---
@@ -58,11 +58,12 @@ The epic is split into seven stories, one per milestone (US-001.01 to US-001.07)
- Given the repositories exist, when the script finishes, then the project directory has one remote, `origin` (Gitea), with or without GitHub, containing no credential; there is no `github` remote, because a push to `origin` reaches GitHub through the mirror.
- Given the project directory, when the script finishes, then the framework, its skills and git hooks (and the plan gate if chosen) and the copied templates are in place.
- Given the framework steps ran, when the script finishes, then git ignores `.claude`, `.agents` and `AGENTS.md` in the project through `.git/info/exclude`: no `.gitignore` or tracked file is changed, nothing is committed, and a path git already tracks stays tracked and is named in the summary.
- Given a directory or file already exists, when the script would replace it, then it asks first.
| Traces to | Size | INVEST exceptions |
| --- | --- | --- |
| [UC-001] steps 8 to 10, [MIL-003] | fits one phase | Independent: needs the repositories of US-001.02 |
| [UC-001] steps 8 to 10, [MIL-003], [MIL-009] | fits one phase | Independent: needs the repositories of US-001.02 |
### US-001.04 — Create a new project: preset the details
@@ -166,6 +167,7 @@ Valuable, Negotiable, Estimable, Small and Testable hold for each story. Indepen
[MIL-006]: ./milestones/mil-006-project-license.md
[MIL-007]: ./milestones/mil-007-framework-checklists.md
[MIL-008]: ./milestones/mil-008-gitea-only-remote.md
[MIL-009]: ./milestones/mil-009-exclude-framework-files.md
[PP-001]: ./project-plan.md
[24f1507]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/24f15070fc73fb06e61865141fe0b825ea9e821e
[039a28c]: https://git.tirsystem.com/TirSystem-BashScript/repo_foundry/commit/039a28c01b56f8cf0af73f55d1a604b43d67ba03
[08cb484]: https://git.tirsystem.com/TirSystem-BashScript/RepoFoundry/commit/08cb484498bab3d9480decda9df89e9564438185
+236
View File
@@ -0,0 +1,236 @@
# How to create the access tokens RepoFoundry needs
RepoFoundry talks to two hosts and needs one token for each:
| Token | Key | Needed | Type |
| --- | --- | --- | --- |
| Gitea access token | `GITEA_TOKEN` | always | an access token with scopes |
| GitHub personal access token | `GITHUB_PAT` | only when you choose GitHub | a **classic** personal access token (PAT) |
This guide shows both, step by step. The red numbers in each picture match the
numbered steps next to it.
> **About the pictures.** They are drawings of the pages, not screenshots, so
> that they show no account data. Names, colors and the position of a control
> can differ a little in your version of Gitea or GitHub. The words in bold in
> the steps are the labels to look for. A new token is shown as dots in the
> pictures; on your screen you see its real value.
The full list of what each token may do is in
[Token permissions](../README.md#token-permissions) in the README. This guide
is the click-by-click version of it.
Contents:
1. [Create a Gitea access token](#1-create-a-gitea-access-token)
2. [Create a GitHub personal access token (classic)](#2-create-a-github-personal-access-token-classic)
3. [Give the tokens to RepoFoundry](#3-give-the-tokens-to-repofoundry)
4. [Keep the tokens safe](#4-keep-the-tokens-safe)
5. [If something goes wrong](#5-if-something-goes-wrong)
---
## 1. Create a Gitea access token
The examples use `https://git.tirsystem.com`. If your Gitea runs elsewhere, use
the address you set as `GITEA_URL` in `config.env`.
### Step 1. Open your settings
Sign in to Gitea. Click your **avatar** in the top-right corner (1), then
choose **Settings** (2).
![Step 1: the avatar menu in the top-right corner, with Settings highlighted](img/gitea-1-open-settings.svg)
### Step 2. Open "Applications"
In the menu on the left, click **Applications** (1).
You can also go straight there: `<your Gitea address>/user/settings/applications`.
![Step 2: the Applications entry in the left menu of the settings](img/gitea-2-applications.svg)
### Step 3. Fill in the token form
Scroll to **Generate New Token** and fill it in:
1. **Token Name:** a name you will recognise later, for example `RepoFoundry`.
2. **Repository and Organization Access:** choose **All (public, private, and
limited)**. A token that is limited to **Public only** cannot see private
repositories or organizations, so creating a private project would fail.
3. **Select permissions:** set these three to **Read and Write** and leave
every other line at **No Access**:
| Permission | Level | Why RepoFoundry needs it |
| --- | --- | --- |
| `organization` | Read and Write | look up the owner and your rights in it, create repositories in an organization |
| `repository` | Read and Write | create the repository and manage its push mirror |
| `user` | Read and Write | read which account the token belongs to, and create a repository under **your own** account |
4. Click **Generate Token**.
![Step 3: the token form with the name, the access choice and three permissions set to Read and Write](img/gitea-3-token-form.svg)
> **Only creating projects in organizations?** Then `user` can stay at **Read**.
> `Read and Write` on `user` is needed only to create a repository under your
> own account; without it Gitea answers `required=[write:user]`.
### Step 4. Copy the token now
Gitea shows the new token **once**, in a green message at the top of the page
(1). Copy it (use the copy button if your version has one, or select the text
and press Ctrl+C, Cmd+C on a Mac) and keep it for
[step 3 of this guide](#3-give-the-tokens-to-repofoundry). The new token is now
in the list below it (2), with its permissions, but the list never shows its
value again.
If you lose the value, delete that token and generate a new one.
![Step 4: the green message with the new token and a Copy button, and the token in the list below](img/gitea-4-copy-token.svg)
---
## 2. Create a GitHub personal access token (classic)
Skip this part when you will not create a GitHub repository (`USE_GITHUB=no` or
answering no when asked). `GITHUB_PAT` is then not needed.
Use a **classic** token. RepoFoundry has not been tested with fine-grained
tokens, because GitHub documents no fine-grained permission for creating a
repository.
### Step 1. Open your settings
Sign in to GitHub. Click your **profile picture** in the top-right corner (1),
then **Settings** (2).
![Step 1: the profile menu in the top-right corner, with Settings highlighted](img/github-1-open-settings.svg)
### Step 2. Open "Developer settings"
In the left sidebar, scroll to the bottom and click **Developer settings** (1).
It is the last entry.
![Step 2: Developer settings at the bottom of the left sidebar](img/github-2-developer-settings.svg)
### Step 3. Choose "Tokens (classic)"
1. In the left sidebar, open **Personal access tokens** and click **Tokens
(classic)** (1).
2. Click **Generate new token** (2).
3. In the menu that opens, choose **Generate new token (classic)** (3). Not
the first entry, which is the fine-grained kind.
GitHub may ask you to confirm your password or a two-factor code. Do that
yourself; nobody else should type it.
![Step 3: Tokens (classic) in the sidebar, and Generate new token (classic) in the button menu](img/github-3-tokens-classic.svg)
> Direct address: `https://github.com/settings/tokens/new`
### Step 4. Fill in the form
1. **Note:** a name you will recognise later, for example `RepoFoundry`.
2. **Expiration:** pick a date. A shorter life limits the damage if the token
leaks; you then create a new token when it ends.
3. **Select scopes:** tick the scopes below and nothing else.
| Scope | Tick it when | Why |
| --- | --- | --- |
| `repo` | always (private or public projects) | creates the repository, and is the password Gitea uses to push the mirror |
| `public_repo` instead of `repo` | **only** public projects | enough to create and push a public repository; ticking `repo` already includes it |
| `read:org` | only if the script says you do not belong to your organization | lets the script check your membership of an organization owner |
Ticking `repo` also ticks its five sub-scopes (`repo:status`,
`repo_deployment`, `public_repo`, `repo:invite`, `security_events`); that is
expected. Leave `workflow`, `write:packages` and the rest unticked.
4. Scroll down and click **Generate token** (4).
![Step 4: the new token form with a note, an expiration, the repo scope ticked and the Generate token button](img/github-4-token-form.svg)
> **`read:org` and `admin:org`.** The README records that the check worked with
> a token that had `repo` and `admin:org`, and that `read:org` alone is
> untested. Try `read:org` first. `admin:org` gives far more power than
> RepoFoundry needs, so use it only if `read:org` is not enough.
### Step 5. Copy the token now
GitHub shows the token **once**, in a green box at the top (1). Click the copy
icon next to it. It starts with `ghp_`. Keep it for
[step 3 of this guide](#3-give-the-tokens-to-repofoundry).
If the owner of the new repository is an organization that uses SAML single
sign-on, the token must also be authorised for that organization: in the token
list click **Configure SSO** (2) next to the token and then **Authorize**
for the organization.
![Step 5: the green box with the new token and a copy icon, and Configure SSO in the token list](img/github-5-copy-token.svg)
---
## 3. Give the tokens to RepoFoundry
Choose one way. Both keep the token out of `config.env` (a token there is
rejected).
**Option A: type it when asked (nothing is stored).** Do nothing in advance.
RepoFoundry asks for `Gitea access token` at the start, and for
`GitHub personal access token` and `GitHub account name` once you choose
GitHub. What you type is not shown on the screen. Paste only the token, with no
spaces, no line break and no quote marks.
**Option B: keep them in `.env` (convenient, plain text on disk).** In the
RepoFoundry folder:
```bash
cp .env.example .env
chmod 600 .env # Linux and macOS: only you can read it
```
Open `.env` and fill in the values; replace the text in angle brackets and
remove the brackets:
```text
GITEA_TOKEN=<the Gitea token>
GITHUB_PAT=<the GitHub token>
GITHUB_USER=<your GitHub account name>
```
`GITHUB_PAT` and `GITHUB_USER` are needed only when you create a GitHub
repository. Git ignores `.env`. Never commit it, send it, or paste it into a
chat or an issue.
Then check the setup with a dry run, which only reads from the hosts and
creates nothing:
```bash
src/create-project.sh
```
---
## 4. Keep the tokens safe
- A token is a password. Anyone who has it can do what it allows.
- Give each token only the access in this guide, and an expiration date.
- Do not paste a token in a command line, a URL, a commit, an issue or a chat.
- If a token may have leaked, delete it at once and make a new one:
- Gitea: **Settings**, **Applications**, then **Delete** on that token.
- GitHub: **Settings**, **Developer settings**, **Personal access tokens**,
**Tokens (classic)**, then **Delete** on that token.
- When a token expires, create a new one the same way and replace it in `.env`.
---
## 5. If something goes wrong
| What you see | Likely cause | What to do |
| --- | --- | --- |
| `authentication failed: the token is missing, expired or invalid` | the token was mistyped, has expired or was deleted | create a new token and use it |
| `the token is valid but not allowed to do this (check its scopes)` | a permission is missing | Gitea: set `organization`, `repository` and `user` to **Read and Write**. GitHub: tick `repo` |
| Gitea answers `required=[write:user]` | the project is being created under your own account | set `user` to **Read and Write** |
| `Gitea owner '...' is neither your account (...) nor an organization the token can see` | the token is limited to **Public only**, or the owner name is wrong | make the token **All (public, private, and limited)**, or fix the owner |
| `GitHub owner '...' is neither your account (...) nor an organization you belong to (or the token lacks the read:org scope)` | the token cannot see the organization | tick `read:org`, and authorise the token for the organization if it uses single sign-on |
| the pasted token is refused again and again | spaces, a line break or quote marks came with the paste | copy it again and paste only the token |
| `GITHUB_USER is '...' but the token belongs to '...'` | a warning: the name in `.env` is not the account of the token | no action needed; RepoFoundry uses the account the token belongs to |
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

+1
View File
@@ -19,6 +19,7 @@ create_all() {
add_framework
install_framework
copy_templates
exclude_framework_files
create_env_file
}
+6 -2
View File
@@ -8,7 +8,7 @@
# shellcheck disable=SC2034 # read and written by the other library files
readonly PROJECT_NAME="${REPOFOUNDRY_NAME:-RepoFoundry}"
readonly VERSION="0.3.1"
readonly VERSION="0.3.2"
readonly EXIT_FAILURE=1
readonly EXIT_USAGE=2
readonly MAX_VALUE_LENGTH=2048
@@ -30,8 +30,12 @@ readonly HINT_LICENSE="use a Gitea license key (letters, digits, '.', '+' or '-'
readonly HINT_DIRECTORY="must not be empty, start with '-' or contain control characters"
readonly HINT_TOKEN="8 to 255 letters, digits or _ . ~ + / = -"
readonly ENV_FILE_NAME=".env"
# What the framework installs into the new project that git must not list: the
# folders of the skills and the copy of AGENTS.md, anchored to the project
# root. They go into .git/info/exclude of the new project.
readonly FRAMEWORK_EXCLUDES=(/.claude /.agents /AGENTS.md)
readonly PLAN_STEPS=("GitHub repository" "Gitea repository" "Push mirror"
"Local project" "Framework" "Skills and hooks" "Templates" "Project .env")
"Local project" "Framework" "Skills and hooks" "Templates" "Git excludes" "Project .env")
# shellcheck disable=SC2034 # read through namerefs (parse_env_file)
readonly CONFIG_KEYS=(GITHUB_API_URL GITHUB_WEB_URL GITEA_URL GITEA_API_URL
GITEA_SSH_PORT MIRROR_INTERVAL FRAMEWORK_REPO
+2 -13
View File
@@ -26,19 +26,8 @@ env_file_key_list() {
# file: the entry goes into .git/info/exclude, which is never committed. It
# does nothing when .env is already ignored.
exclude_env_file() {
local dir="$1" gitdir exclude
if git_project "$dir" check-ignore -q -- "$ENV_FILE_NAME"; then
return 0
fi
gitdir="$(git_project "$dir" rev-parse --absolute-git-dir)"
exclude="$gitdir/info/exclude"
mkdir -p -- "$gitdir/info"
# Start on a fresh line when the file does not end with one.
if [[ -s $exclude && -n "$(tail -c 1 -- "$exclude")" ]]; then
printf '\n' >>"$exclude"
fi
printf '%s\n' "# RepoFoundry: the credentials file of this project" "$ENV_FILE_NAME" >>"$exclude"
git_project "$dir" check-ignore -q -- "$ENV_FILE_NAME" ||
local dir="$1"
exclude_from_git "$dir" "RepoFoundry: the credentials file of this project" "$ENV_FILE_NAME" ||
die "could not make git ignore $ENV_FILE_NAME in $dir; nothing was written to it"
}
+37 -1
View File
@@ -4,7 +4,7 @@
#
# Part of create-project.sh: sourced by it, never run on its own.
#
# Provides: is_framework_skipped, init_framework_submodules, add_framework, run_framework_script, install_skills, install_hooks, install_framework, copy_template, copy_templates
# Provides: is_framework_skipped, init_framework_submodules, add_framework, run_framework_script, install_skills, install_hooks, install_framework, copy_template, copy_templates, exclude_framework_files
# is_framework_skipped: succeed when the framework steps are left out because
# SSH to Gitea is not available (the Maintainer agreed to that).
@@ -165,3 +165,39 @@ copy_templates() {
notes="$notes; ${STATE[template_note]}"
finish_step "$label" "created" "($notes)"
}
# exclude_framework_files: make git ignore .claude, .agents and AGENTS.md in
# the new project, through its .git/info/exclude (never .gitignore, so no
# tracked file changes and nothing is committed). Only when the framework
# steps ran. A path git already tracks stays tracked: an exclusion does not
# apply to it, so the step names it.
exclude_framework_files() {
local label="Git excludes" dir="${PROJECT[directory]}" entry path tracked=() note written
if is_framework_skipped; then
finish_step "$label" "skipped" "(no SSH access to Gitea)"
return 0
fi
begin_step "$label"
exclude_from_git "$dir" "RepoFoundry: the files installed from the framework" "${FRAMEWORK_EXCLUDES[@]}" ||
die "could not make git ignore the framework files in $dir; check $dir/.git/info/exclude"
written="$REPLY"
for entry in "${FRAMEWORK_EXCLUDES[@]}"; do
path="${entry#/}"
if [[ -n "$(git_project "$dir" ls-files -- "$path")" ]]; then
tracked+=("$path")
fi
done
if ((written == 0)); then
note="${FRAMEWORK_EXCLUDES[*]} already excluded"
else
note="${FRAMEWORK_EXCLUDES[*]} added to .git/info/exclude"
fi
if ((${#tracked[@]})); then
note="$note; git tracks ${tracked[*]}, so it is not ignored"
fi
if ((written == 0)); then
finish_step "$label" "reused" "($note)"
else
finish_step "$label" "created" "($note)"
fi
}
+35 -1
View File
@@ -4,7 +4,7 @@
#
# Part of create-project.sh: sourced by it, never run on its own.
#
# Provides: git_project, fetch_origin
# Provides: git_project, exclude_from_git, fetch_origin
# git_project DIR ARGS...: run git in DIR. Prompts are switched off and stdin
# is closed (git must not eat the answers meant for later prompts), so a
@@ -16,6 +16,40 @@ git_project() {
git -C "$dir" "$@" </dev/null
}
# exclude_from_git DIR COMMENT ENTRY...: make git ignore each ENTRY in DIR
# without touching a tracked file: the entries go into .git/info/exclude,
# which is never committed. An entry that already takes effect (checked by its
# pattern, so a tracked path counts) is left out, and with none left nothing
# is written. The comment line goes above the entries that are written. The
# number of entries written is left in REPLY; the return status is 1 when an
# entry still does not take effect afterwards.
exclude_from_git() {
local dir="$1" comment="$2" gitdir exclude entry path missing=()
shift 2
for entry in "$@"; do
path="${entry#/}"
if ! git_project "$dir" check-ignore --no-index -q -- "${path%/}"; then
missing+=("$entry")
fi
done
REPLY="${#missing[@]}"
if ((REPLY == 0)); then
return 0
fi
gitdir="$(git_project "$dir" rev-parse --absolute-git-dir)"
exclude="$gitdir/info/exclude"
mkdir -p -- "$gitdir/info"
# Start on a fresh line when the file does not end with one.
if [[ -s $exclude && -n "$(tail -c 1 -- "$exclude")" ]]; then
printf '\n' >>"$exclude"
fi
printf '%s\n' "# $comment" "${missing[@]}" >>"$exclude"
for entry in "${missing[@]}"; do
path="${entry#/}"
git_project "$dir" check-ignore --no-index -q -- "${path%/}" || return 1
done
}
# fetch_origin DIR: fetch the Gitea repository into the project. Over SSH the
# user's key is used. Over HTTPS the token reaches git through a private
# GIT_ASKPASS helper and the environment of this one command: it is never part
+1
View File
@@ -51,6 +51,7 @@ print_plan() {
say "$(printf ' %-18s: %s' "Framework" "add $(framework_url) as a submodule")"
say "$(printf ' %-18s: %s' "Skills and hooks" "install once; plan gate $(yes_no "${PROJECT[is_plan_gate_enabled]}")")"
say "$(printf ' %-18s: %s' "Templates" "AGENTS.md and docs/artifact-registry.md (you are asked before a file is replaced)")"
say "$(printf ' %-18s: %s' "Git excludes" "${FRAMEWORK_EXCLUDES[*]} go into .git/info/exclude (no tracked file changes)")"
else
say "$(printf ' %-18s: %s' "Framework" "NOT possible without SSH to Gitea; you will be asked whether to go on without it")"
fi
+1
View File
@@ -291,6 +291,7 @@ echo done'
local exclude
exclude="$(cat "$WORK/p/.git/info/exclude")"
assert_contains "old entry kept" "$exclude" "build/"
assert_contains "comment line" "$exclude" "# RepoFoundry: the credentials file of this project"
assert_eq "entry added once" "1" "$(grep -c '^\.env$' "$WORK/p/.git/info/exclude")"
assert_eq "old entry still on its own line" "1" "$(grep -c '^build/$' "$WORK/p/.git/info/exclude")"
}
+104
View File
@@ -282,6 +282,105 @@ test_existing_template_targets_are_replaced_only_after_a_yes() {
assert_contains "reported" "$OUT" "kept existing AGENTS.md; copied docs/artifact-registry.md"
}
# ------------------------------------------------------------- git excludes
# exclude_count FILE LINE: how many lines of FILE are exactly LINE.
exclude_count() {
grep -cxF -e "$2" "$1" || true
}
test_the_framework_files_are_ignored_by_git_and_nothing_else_changes() {
setup_hosts
local dir="$WORK/project" exclude path listing
exclude="$dir/.git/info/exclude"
local_answers "$dir" y n
# create now, and create the project's .env as well
run_apply "$LOCAL_ANSWERS"$'y\ny\n'
assert_status "apply" 0 "$STATUS"
assert_contains "reported" "$OUT" "Git excludes : created (/.claude /.agents /AGENTS.md added to .git/info/exclude)"
for path in /.claude /.agents /AGENTS.md; do
assert_eq "one entry for $path" "1" "$(exclude_count "$exclude" "$path")"
done
for path in .claude .agents AGENTS.md .claude/skills/coding-conventions/SKILL.md .agents/skills/.framework-skills; do
check
if ! project_git "$dir" check-ignore -q -- "$path"; then
fail "$path is not ignored by git"
fi
done
for path in framework .gitmodules docs/artifact-registry.md; do
check
if project_git "$dir" check-ignore -q --no-index -- "$path"; then
fail "$path is ignored by git but is part of the project"
fi
done
listing="$(project_git "$dir" status --porcelain)"
assert_not_contains ".claude is not listed" "$listing" ".claude"
assert_not_contains ".agents is not listed" "$listing" ".agents"
assert_not_contains "AGENTS.md is not listed" "$listing" "AGENTS.md"
assert_contains "the submodule is listed" "$listing" "framework"
assert_contains "the registry is listed" "$listing" "docs/"
assert_file_missing "no .gitignore is written" "$dir/.gitignore"
assert_eq "nothing was committed" "1" "$(project_git "$dir" rev-list --count HEAD)"
assert_eq "the .env keeps its own entry, once" "1" "$(exclude_count "$exclude" ".env")"
assert_contains "the .env comment is unchanged" "$(cat "$exclude")" "# RepoFoundry: the credentials file of this project"
}
test_the_git_excludes_are_written_once_and_keep_the_existing_lines() {
local dir="$WORK/p" exclude="$WORK/p/.git/info/exclude" path
mkdir -p "$dir"
project_git "$dir" init -q
printf 'build/' >"$exclude" # no trailing newline
run_lib "" 'PROJECT[directory]="'"$dir"'"
exclude_framework_files
echo "${STEP_STATUS["Git excludes"]}"
exclude_framework_files
echo "${STEP_STATUS["Git excludes"]} ${STEP_DETAIL["Git excludes"]}"'
assert_status "run" 0 "$STATUS"
assert_eq "created, then reused" $'created\nreused (/.claude /.agents /AGENTS.md already excluded)' "$OUT"
assert_eq "the old line is kept on its own line" "1" "$(exclude_count "$exclude" "build/")"
for path in /.claude /.agents /AGENTS.md; do
assert_eq "entry for $path written once" "1" "$(exclude_count "$exclude" "$path")"
done
assert_eq "one comment line" "1" "$(exclude_count "$exclude" "# RepoFoundry: the files installed from the framework")"
}
test_nothing_is_written_for_paths_git_already_ignores() {
local dir="$WORK/p" exclude="$WORK/p/.git/info/exclude"
mkdir -p "$dir"
project_git "$dir" init -q
printf '.claude\n.agents\nAGENTS.md\n' >"$dir/.gitignore"
run_lib "" 'PROJECT[directory]="'"$dir"'"
exclude_framework_files
echo "${STEP_STATUS["Git excludes"]}"'
assert_status "run" 0 "$STATUS"
assert_eq "reused" "reused" "$OUT"
assert_eq "no entry written" "0" "$(grep -c '^/' "$exclude" || true)"
assert_eq "no comment written" "0" "$(exclude_count "$exclude" "# RepoFoundry: the files installed from the framework")"
}
test_a_tracked_framework_file_stays_tracked_and_is_named() {
local dir="$WORK/p"
mkdir -p "$dir/.agents" "$dir/.claude"
project_git "$dir" init -q
printf 'mine\n' >"$dir/AGENTS.md"
printf 'x\n' >"$dir/.agents/keep"
printf 'y\n' >"$dir/.claude/new" # not tracked
project_git "$dir" add AGENTS.md .agents/keep
project_git "$dir" commit -q -m seed
run_lib "" 'PROJECT[directory]="'"$dir"'"
exclude_framework_files
echo "${STEP_STATUS["Git excludes"]} ${STEP_DETAIL["Git excludes"]}"'
assert_status "run" 0 "$STATUS"
assert_contains "names the tracked paths" "$OUT" "git tracks .agents AGENTS.md, so it is not ignored"
assert_eq "both are still tracked" $'.agents/keep\nAGENTS.md' "$(project_git "$dir" ls-files)"
assert_eq "the content is unchanged" "mine" "$(cat "$dir/AGENTS.md")"
assert_eq "nothing is staged or modified" "" "$(project_git "$dir" status --porcelain)"
check
if ! project_git "$dir" check-ignore -q -- .claude; then
fail ".claude, which git does not track, is not ignored"
fi
}
# ----------------------------------------------------------- SSH and errors
test_without_ssh_the_run_stops_unless_the_framework_is_skipped() {
@@ -311,6 +410,9 @@ test_without_ssh_the_framework_steps_are_skipped_after_a_yes() {
assert_contains "framework skipped" "$OUT" "Framework : skipped (no SSH access to Gitea)"
assert_contains "skills and hooks skipped" "$OUT" "Skills and hooks : skipped (no SSH access to Gitea)"
assert_contains "templates skipped" "$OUT" "Templates : skipped (no SSH access to Gitea)"
assert_contains "git excludes skipped" "$OUT" "Git excludes : skipped (no SSH access to Gitea)"
assert_not_contains "nothing excluded: .claude" "$(cat "$dir/.git/info/exclude")" "/.claude"
assert_not_contains "nothing excluded: AGENTS.md" "$(cat "$dir/.git/info/exclude")" "/AGENTS.md"
assert_file_missing "no submodule" "$dir/.gitmodules"
assert_file_missing "no AGENTS.md" "$dir/AGENTS.md"
}
@@ -365,6 +467,7 @@ test_the_dry_run_plan_describes_the_local_steps_and_creates_nothing() {
assert_contains "framework" "$OUT" "Framework : add $SSH_FRAMEWORK_URL as a submodule"
assert_contains "skills and hooks" "$OUT" "Skills and hooks : install once; plan gate yes"
assert_contains "templates" "$OUT" "Templates : AGENTS.md and docs/artifact-registry.md (you are asked before a file is replaced)"
assert_contains "git excludes" "$OUT" "Git excludes : /.claude /.agents /AGENTS.md go into .git/info/exclude (no tracked file changes)"
assert_file_missing "nothing created" "$dir"
}
@@ -378,6 +481,7 @@ test_the_plan_marks_an_existing_directory_and_missing_ssh() {
run_dry "$LOCAL_ANSWERS"
assert_contains "existing directory" "$OUT" "use the existing directory $dir, which has files (you will be asked)"
assert_contains "no SSH" "$OUT" "NOT possible without SSH to Gitea; you will be asked whether to go on without it"
assert_not_contains "no git excludes without the framework" "$OUT" "Git excludes"
assert_contains "HTTPS origin" "$OUT" "will use HTTPS (SSH test: failed"
}
+1 -1
View File
@@ -103,7 +103,7 @@ test_usage_errors() {
assert_contains "help shows exit codes" "$OUT" "Exit codes"
run_cli "" --version
assert_status "version" 0 "$STATUS"
assert_contains "version output" "$OUT" "RepoFoundry 0.3.1"
assert_contains "version output" "$OUT" "RepoFoundry 0.3.2"
}
test_warns_when_env_is_not_ignored_by_git() {