230 lines
16 KiB
Markdown
230 lines
16 KiB
Markdown
# #1043 — Fleet pane git-identity propagation
|
||
|
||
## Objective
|
||
|
||
Ensure a fleet seat's launched runtime process receives its roster-derived `MOSAIC_GIT_IDENTITY`, and lock the complete generated-environment propagation boundary with an enumerated set comparison.
|
||
|
||
## Tracking
|
||
|
||
- External issue: `mosaicstack/stack#1043`
|
||
- Branch: `fix/1043-pane-git-identity`
|
||
- Coordinator: `tl-mosaic`
|
||
- `docs/TASKS.md`: read-only by project worker contract; not modified.
|
||
|
||
## Constraints
|
||
|
||
- RED-first bug reproducer is mandatory.
|
||
- R7 delete-the-subject mutation must turn the behavioral test red.
|
||
- Assert launched-process environment, not source text.
|
||
- One push only; do not poll CI after push.
|
||
- Run the CI queue guard immediately before push and report its `state=` line as state, not evidence.
|
||
- Do not modify a live host launcher or obtain/copy another credential.
|
||
- Self-post the PR, verify provider attribution, then stop.
|
||
- Final status wording: `believed-fixed, pending jarvis validation`.
|
||
|
||
## Scope inventory
|
||
|
||
Re-derived against `origin/main` at `85d2108e`:
|
||
|
||
- Launch consumer: `packages/mosaic/framework/tools/fleet/start-agent-session.sh`
|
||
- Behavioral launch test: `packages/mosaic/framework/tools/fleet/test-start-agent-session.sh`
|
||
- Generated-environment contract/parser: `packages/mosaic/src/fleet/generated-env-boundary.ts`
|
||
- Roster projection producers:
|
||
- `packages/mosaic/src/commands/fleet.ts`
|
||
- `packages/mosaic/src/fleet/fleet-reconciler.ts`
|
||
- `packages/mosaic/src/fleet/fleet-agent-crud.ts`
|
||
- `packages/mosaic/src/fleet/v1-v2-migration.ts`
|
||
- Contract and producer tests discovered by repository search.
|
||
- Generated-environment operator/developer docs and their executable documentation contract test.
|
||
|
||
Discrepancy sent to `tl-mosaic`: current main no longer contains the charter's `PANE_SHELL_SNIPPET`; #772 replaced it with an `/usr/bin/env -i` argv launch boundary, and current generated projections do not declare git identity. Code-read inventory is **NOT MEASURED** behavior.
|
||
|
||
## Plan
|
||
|
||
1. Add the process-environment set-comparison regression first and record RED.
|
||
2. Add roster-derived `MOSAIC_GIT_IDENTITY=<agent name>` to the complete generated projection contract.
|
||
3. Validate identity syntax and equality with `MOSAIC_AGENT_NAME`; pass it through the clean pane environment.
|
||
4. Update affected projection tests and generated-environment docs.
|
||
5. Run focused and baseline gates.
|
||
6. Perform R7 by deleting the pane propagation entry, prove RED, restore, and prove GREEN.
|
||
7. Run independent review, remediate, commit, queue guard, one push, self-post PR, verify provider attribution, and stop without CI polling.
|
||
|
||
## Budget
|
||
|
||
No explicit token cap was provided. Working cap: one narrow logical unit, no dependency installation unless existing tooling requires it, no unrelated refactor.
|
||
|
||
## Evidence log
|
||
|
||
### TDD and mutation evidence
|
||
|
||
- RED-first, repository launcher: `bash packages/mosaic/framework/tools/fleet/test-start-agent-session.sh` exited 64 on pre-fix source with `code=unknown-key key=MOSAIC_GIT_IDENTITY`. The generated seat could not launch with the required declared identity.
|
||
- GREEN: the same repository launcher test emitted `ok - start-agent-session generated environment boundary`.
|
||
- R7 delete-the-subject: removed only `"MOSAIC_GIT_IDENTITY=$MOSAIC_GIT_IDENTITY"` from the repository launch array; the same test exited 1 with `FAIL: runtime pane omitted or changed generated environment keys: MOSAIC_GIT_IDENTITY`.
|
||
- R7 restoration: restored that launch entry; the same test returned green.
|
||
- Launcher under test is explicitly `packages/mosaic/framework/tools/fleet/start-agent-session.sh` through the test's `$START`, **not** the stale installed host copy.
|
||
|
||
### Situational and focused tests
|
||
|
||
- Repository launcher boundary: green, including set comparison of all nine generated projection entries and fail-before-tmux cases for missing, unsafe, mismatched, and local-shadow Git identity.
|
||
- Fleet systemd launcher integration: `bash packages/mosaic/framework/systemd/user/test-fleet-units.sh` — green.
|
||
- Focused Mosaic Vitest set: 6 files, 311 tests — green.
|
||
- `bash -n` on changed shell files — green.
|
||
- `git diff --check` — green.
|
||
|
||
### Baseline gates
|
||
|
||
- `pnpm typecheck` — 45/45 tasks green.
|
||
- `pnpm lint` — 25/25 tasks green.
|
||
- `pnpm format:check` — green.
|
||
- `pnpm test:checkout` — green.
|
||
- Repository-wide Vitest under a hermetic current-version npm prefix: Mosaic 81/81 files and 1510/1510 tests green; other workspace test tasks shown green before the framework-shell phase.
|
||
- Canonical `pnpm test` is not fully green on this host for unrelated environment-sensitive gates:
|
||
1. the first two runs exposed the globally installed Mosaic 0.0.48 update banner in three CLI smoke tests expecting empty stderr;
|
||
2. after isolating that global-version input, the framework wake assertion aborted at the known `#973` Bash `BASH_LINENO` convention check (exit 97; observed `[3 5]`, expected `[3 4]`).
|
||
No tests were weakened or bypassed; focused changed-surface tests are green. CI remains the canonical clean-environment result and is intentionally not polled after push per charter.
|
||
|
||
### Independent review
|
||
|
||
- Codex code review first pass: request changes for missing shell rejection-path coverage.
|
||
- Remediation: added table-driven missing/unsafe/mismatch/local-shadow launcher cases, each asserting no tmux call.
|
||
- Codex code re-review: **approve**, no findings, confidence 0.88.
|
||
- Codex security review: risk `none`, no findings, confidence 0.97.
|
||
|
||
### Acceptance criteria mapping
|
||
|
||
| Acceptance criterion | Evidence |
|
||
| --- | --- |
|
||
| AC-FGI-01: launched process receives every generated key/value | Repository launcher process-environment `comm -23` set comparison; GREEN and R7 RED evidence above |
|
||
| AC-FGI-02: missing, unsafe, or split identity fails before tmux | Table-driven shell cases plus TypeScript generated-boundary tests |
|
||
| AC-FGI-03: focused/baseline/review evidence recorded | Commands and review outcomes above; host-sensitive full-suite limitations stated explicitly |
|
||
|
||
### Documentation checklist
|
||
|
||
- PRD updated with #1043 requirements and acceptance criteria.
|
||
- Fleet launch runbook, generated-env concept, and generated-env reference updated.
|
||
- No API/OpenAPI, sitemap, user publishing target, deployment, or external docs publication change applies.
|
||
- `docs/TASKS.md` remains unmodified per its single-writer project contract.
|
||
|
||
## Round 2 — PR #1073 review 97 remediation
|
||
|
||
### Review blocker
|
||
|
||
The launched-process suite was signed-excluded from CI enumeration. Manual GREEN/R7 evidence therefore did not prove a PR workflow could detect regression.
|
||
|
||
### RED-first and canonical wiring
|
||
|
||
1. Removed the suite's signed exclusion before adding a CI execution path.
|
||
2. `check-test-enumeration.sh` went RED with exact `UNENUMERATED` output for `test-start-agent-session.sh`: population 49, enumerated 30, excluded 18.
|
||
3. Added both `framework/tools/fleet/test-start-agent-session.sh` and `framework/systemd/user/test-fleet-units.sh` to `@mosaicstack/mosaic`'s canonical `test:framework-shell` chain.
|
||
4. The guard returned GREEN: population 49, enumerated 32, excluded 18, surfaces 45. The systemd suite is outside the guard's tools-only population but now has the same explicit canonical execution disposition.
|
||
|
||
### Workflow-level R7
|
||
|
||
- Deleted only the pane launch entry `"MOSAIC_GIT_IDENTITY=$MOSAIC_GIT_IDENTITY"`.
|
||
- Ran the exact `.woodpecker/ci.yml` test-step command, `pnpm test`, with only a temporary PATH-scoped npm shim reporting the checkout's current 0.0.49 version so the unrelated global 0.0.48 banner could not preempt the shell chain.
|
||
- Result: exit 1 at `@mosaicstack/mosaic#test`, with the enumeration guard GREEN followed by `FAIL: runtime pane omitted or changed generated environment keys: MOSAIC_GIT_IDENTITY`.
|
||
- Restored the launch entry. The canonical `test:framework-shell` chain then reached both newly wired suites and printed both GREEN markers before the known unrelated #973 host-only `BASH_LINENO` abort.
|
||
- An actual provider PR workflow on the intentionally broken mutant is **NOT MEASURED**: the one-push constraint forbids pushing a red mutant and then a repaired head. Local execution proves the exact PR workflow command and dependency chain go RED on the subject deletion; CI on the repaired pushed head remains canonical.
|
||
|
||
### Workflow population
|
||
|
||
- **DEFINED:** 3 workflows (`ci.yml`, `ci-image.yml`, `publish.yml`).
|
||
- **ELIGIBLE for `pull_request`:** 1/3 (`ci.yml`), based on top-level `when:` clauses.
|
||
- **REPORTED:** Round-1 exact-head provider read reported 1/1 eligible context (`ci/woodpecker/pr/ci`). Post-remediation-head reported count is **NOT MEASURED** by this seat because CI polling is prohibited; workflow definitions and eligibility did not change.
|
||
|
||
### Independent remediation review
|
||
|
||
- First Round-2 review identified a CI-image blocker: the newly wired launcher suite used Perl, which the Alpine CI base does not install.
|
||
- Replaced the suite's three Perl-only fixture mutations with POSIX/BusyBox-compatible `sed -i` substitutions; production behavior and assertions are unchanged.
|
||
- Codex re-review: **APPROVE**, confidence 0.93, no findings.
|
||
|
||
### Vitest denominator reconciliation
|
||
|
||
The PR's `311/311` is correct for its explicitly named six-file command at both the original and remediation worktrees:
|
||
|
||
- generated environment boundary: 24
|
||
- fleet documentation: 23
|
||
- Tess service profile: 6
|
||
- fleet regen command: 27
|
||
- fleet agent CRUD command: 22
|
||
- fleet command: 209
|
||
- total: **311**
|
||
|
||
Review 97 reported 312/312 without naming its six files. That is a different or miscounted population and cannot replace the command-scoped 311 denominator; the PR follow-up will name the exact files and arithmetic.
|
||
|
||
## Round 3 — Alpine stale-marker portability
|
||
|
||
### Objective and plan
|
||
|
||
- Replace the GNU-only relative-date fixture with a deterministic POSIX/BusyBox timestamp while preserving the required stale-marker assertion.
|
||
- Re-run the launcher suite in the canonical `ci-base:latest` Alpine image, then run applicable repository gates and independent review.
|
||
- Update the PR body to name the repeated GNU-host/Alpine-CI portability pattern, run the mandatory queue guard, push once, verify provider attribution, and stop without CI polling.
|
||
- Working budget: 8K tokens; scope is one fixture line plus delivery evidence. No production behavior changes.
|
||
|
||
### RED-first evidence
|
||
|
||
Before the fix, the canonical CI image command
|
||
`docker run --rm -v "$PWD:/work" -w /work git.mosaicstack.dev/mosaicstack/stack/ci-base:latest bash packages/mosaic/framework/tools/fleet/test-start-agent-session.sh`
|
||
exited 1 at the stale-marker setup with exact BusyBox output
|
||
`touch: invalid date '10 seconds ago'`. The prior fresh-marker assertions had already executed, matching pipeline 2233's failure location.
|
||
|
||
### Root cause and fix
|
||
|
||
The test used GNU `touch -d` relative-date parsing although the PR workflow runs on Alpine/BusyBox. The fixture now uses POSIX `touch -t 200001010000.00`, a fixed timestamp that is unconditionally stale; the stale assertion remains mandatory and was not made tolerant of missing timestamp metadata.
|
||
|
||
### Structural pattern
|
||
|
||
This is the third GNU-host/Alpine-CI portability defect in the lane: GNU `grep` multi-match counting, Perl-only fixture mutation, and GNU `touch -d` date parsing. The repeated cause is shell suites authored on a GNU host but executed in an Alpine CI image; durable prevention belongs in CI-image execution or portability lint, not assertion weakening.
|
||
|
||
### GREEN and quality evidence
|
||
|
||
- Focused launcher suite in `ci-base:latest`: exit 0, `ok - start-agent-session generated environment boundary`.
|
||
- Canonical test step in `ci-base:latest` with the pipeline's `pgvector/pgvector:pg17` service, readiness check, migration, and `pnpm test`: exit 0; 46/46 Turbo tasks; Mosaic 81/81 files and 1510/1510 tests; Gateway 57 passed/5 skipped files and 629 passed/11 skipped tests; enumeration 49 population / 32 enumerated / 18 signed exclusions / 45 named surfaces.
|
||
- The first image-only `pnpm test` attempt lacked the pipeline PostgreSQL service and failed only on connection refusal after the launcher suite was GREEN. The rerun supplied the canonical service precondition and passed.
|
||
- Canonical-image baseline: typecheck 45/45 tasks, lint 25/25 tasks, format check GREEN; `git diff --check` GREEN.
|
||
- Independent Codex code review: APPROVE, confidence 0.96, 2/2 Round-3 files, no findings.
|
||
- Independent Codex security review: risk none, confidence 0.99, 2/2 Round-3 files, no findings.
|
||
|
||
### Re-derived inventory and denominators
|
||
|
||
- Round-3 git delta: **2/2 files** — launcher suite and task scratchpad; 25 insertions / 1 deletion before evidence finalization.
|
||
- Full PR path inventory against `origin/main` at `85d2108e`: **19/19 changed paths**; Round 3 adds no new PR path.
|
||
- Workflow definition population: **1/3 pull-request-eligible** (`ci.yml` of `ci.yml`, `ci-image.yml`, `publish.yml`).
|
||
- Do not re-litigate the settled 311/312 populations; both are valid for their separately named Tess6 and CRUD-core7 sets.
|
||
|
||
## Round 4 — bound stale-marker observation
|
||
|
||
### Objective and plan
|
||
|
||
- Make the heartbeat assertion discriminate an initially stale native marker from a fresh marker without changing the production staleness threshold or shortening the polling window.
|
||
- Freeze only the sidecar's numeric observation clock during the stale-fixture arm so elapsed assertion time cannot turn a fresh mutant stale.
|
||
- Prove two independent mutants RED: disable production stale-marker detection while retaining the stale fixture; replace the stale fixture with a fresh marker. Restore the tree and prove GREEN in the canonical Alpine image.
|
||
- Re-derive the changed-path inventory, run applicable quality and independent review gates, commit with environment-only author/committer identity, queue-guard, push once, verify provider attribution using curl stdin config, and stop without CI polling.
|
||
- Working budget: 8K tokens. Scope is the launcher test and its scratchpad evidence; production launcher behavior remains unchanged.
|
||
|
||
### Root cause and bounded observation
|
||
|
||
The 30 × 0.1-second assertion window overlaps the production `now - marker > interval * 2 + 1` threshold at interval 1. Depending on second boundaries and load, a fresh marker can age past the threshold before the assertion ends. A focused pre-fix fresh-mutant attempt returned RED while Review 101's full-suite run returned GREEN; the differing result is itself timing dependence, not a discriminating assertion.
|
||
|
||
The test now supplies a fixed numeric epoch only to the stale-fixture sidecar. Its real marker mtime is still read from the filesystem, but assertion runtime cannot advance `now`. Date formatting still delegates to the image's real `/bin/date`. Neither the production threshold nor the 30 × 0.1-second polling window changed.
|
||
|
||
### Two-mutant RED / restored GREEN
|
||
|
||
All three runs used `git.mosaicstack.dev/mosaicstack/stack/ci-base:latest`:
|
||
|
||
1. **Stale-detection mutant RED:** replaced only the production stale-age predicate with `false` while retaining the fixed stale marker; suite exit 1 with `FAIL: heartbeat sidecar did not resume after native marker became stale or absent`.
|
||
2. **Fresh-marker mutant RED:** replaced only `touch -t 200001010000.00` with fresh `touch`; suite exit 1 with the same failed stale-resumption assertion. The fixed observation epoch kept the mutant fresh throughout all 30 polls.
|
||
3. **Restored tree GREEN:** suite exit 0 with `ok - start-agent-session generated environment boundary`.
|
||
|
||
### Re-derived inventory
|
||
|
||
- Round-4 delta: **2/2 files** — launcher test plus task scratchpad; production launcher delta is empty.
|
||
- Full PR inventory against `origin/main`: **19/19 paths**; Round 4 adds no path.
|
||
- Production stale threshold remains `now - marker > iv * 2 + 1`; assertion polling remains 30 × 0.1 seconds.
|
||
- Review 101's confirmed enumeration/workflow/CI and attribution evidence is accepted without re-polling or re-derivation.
|
||
|
||
## Residual risk
|
||
|
||
- Landing on `main` does not update the currently installed host launcher. Host framework installation/reseed and Jarvis live-seat validation are separate downstream events.
|
||
- Canonical CI result is pending and will not be polled by this seat.
|