feat(control-plane): add authoritative native MCP fleet inventory and duplicate-process detection #949

Open
opened 2026-07-27 21:04:33 -05:00 by jcwalker3 · 2 comments
Owner

Related: #900, #948, #686, #689, #708, #634, #636, #430, #887, #929

Problem

The canonical workflow can independently prove revision parity through the five role namespaces, but no sanctioned native capability currently provides authoritative fleet-level evidence for:

  • Exactly one running server per configured PRGS profile
  • Missing servers
  • Duplicate servers
  • Process or worker identity
  • Client/cohort ownership
  • Mixed-client fleets
  • Mixed-revision fleets

Consequently, an LLM cannot satisfy the strict five-process/single-cohort safety gate without relying on shell process inspection, cached JSON, source inspection, or caller-supplied evidence. Those substitutes are not authoritative workflow evidence.

The nearest existing native surface is gitea_assess_mcp_namespace_health, and it is not a substitute: it is scoped to a single named namespace, and it accepts process, probe_result, and registered_tools from the caller. A capability whose inputs are supplied by the party it is meant to constrain cannot serve as authoritative fleet evidence. gitea_get_runtime_context and gitea_assess_master_parity are likewise per-process — each proves something about the server answering the call, and nothing about the other four.

This gap was exposed during cross-LLM validation after PR #944. All five namespaces independently reported parity at 82d71b77028a7abd4f8ab4a4e4d89658a187f73d, but strict fleet validation remained incomplete: five independent self-reports of the same revision do not prove that exactly five processes exist, that no sixth exists, or that all five belong to one client cohort.

Required behavior

Add an authoritative, read-only control-plane capability that reports:

  • Every configured PRGS fleet member
  • Expected namespace and profile
  • Assigned role
  • Repository binding
  • Whether that member is running
  • Stable process or worker identity
  • Runtime instance identity
  • Client/cohort identity
  • Startup revision
  • Current local and remote revision
  • Duplicate instances
  • Missing instances
  • Unexpected instances
  • Mixed-client/cohort membership
  • Mixed-revision membership
  • Overall completeness and health
  • Whether the fleet satisfies the exact-one-instance-per-profile invariant
  • Whether the result is sufficient for mutation-sensitive workflow gates

The expected PRGS fleet is:

  • gitea-author / prgs-author
  • gitea-controller / prgs-controller
  • gitea-reviewer / prgs-reviewer
  • gitea-merger / prgs-merger
  • gitea-reconciler / prgs-reconciler

Exposure and permissions

The capability must:

  • Be exposed natively through gitea-controller
  • Be exposed natively through gitea-reconciler
  • Be strictly read-only
  • Require no shell or OS-process inspection by the caller
  • Return structured machine-readable evidence
  • Fail closed when complete authoritative evidence cannot be obtained
  • Distinguish unknown from healthy
  • Avoid treating profile configuration alone as proof that a server is running
  • Avoid treating parent-client status as proof of individual member health
  • Avoid inferring cohort identity solely from matching revisions

Suggested structured result

Include fields equivalent to:

  • inventory_complete
  • configured_members
  • running_members
  • missing_members
  • duplicate_members
  • unexpected_members
  • mixed_revision
  • mixed_cohort
  • single_cohort
  • exactly_one_per_profile
  • mutation_gate_satisfied
  • blocked_reason
  • Per-member:
    • namespace
    • profile
    • role
    • repository
    • process/worker identity
    • runtime identity
    • client/cohort identity
    • startup head
    • daemon-start head
    • local head
    • live-remote head
    • health

Exact field names may follow existing repository conventions, but equivalent evidence is required.

Acceptance criteria

  1. A healthy five-server fleet reports all five expected members exactly once.
  2. Two processes using the same profile are reported as a duplicate and fail the mutation gate.
  3. A missing expected server is identified and fails the mutation gate.
  4. An unexpected PRGS server is reported explicitly.
  5. Members from different client cohorts are detected.
  6. Members running different startup revisions are detected.
  7. Matching Git revisions alone do not establish a single cohort.
  8. Configuration without a live worker does not count as a running member.
  9. Unknown or unavailable process evidence produces a fail-closed result.
  10. Controller and reconciler return consistent inventory results for the same fleet snapshot.
  11. The capability performs no restart, reconnect, lease mutation, issue mutation, or process termination.
  12. Automated tests cover healthy, duplicate, missing, unexpected, mixed-revision, mixed-cohort, and incomplete-evidence cases.
  13. Canonical workflow documentation explains how controller and reconciler gates consume the result.
  14. An LLM can prove the exact-five-process/single-cohort invariant using only sanctioned native calls.

Non-goals

  • Automatically terminating duplicate processes
  • Restarting or reconnecting the fleet
  • Clearing workflow leases
  • Repairing controller/reconciler role metadata
  • Reconstructing historical restart provenance
  • Cleaning the stale PR #906 author lease

Those are separate workflows or issues.

Duplicate verdict

NOT a duplicate. Verified by reading the bodies and acceptance criteria of #900, #948, #686, #689, #708, #634, #636, #430, and #657.

Issue State Why it does not cover this
#900 open Owns cohort lifecycle supervision — establishing a single eligible cohort, then draining and reaping superseded ones. That is a mutating supervisory transition. This issue is the strictly read-only evidence surface that such a supervisor, and every other gate, would consume. #900 also frames status per cohort; it does not enumerate configured-but-absent fleet members, does not cover repository binding per member, and does not define a mutation_gate_satisfied verdict.
#948 open Defines the ownership model — client/session/generation identity and lease fencing — so that concurrent LLM clients are not falsely blocked. This issue defines the observation surface that reports what is actually running. #948 needs this evidence; it does not provide it.
#686 closed Detection of manually launched duplicates by provenance, per profile, on the serving process. Never enumerates the fleet, never reports missing members, mixed-revision, or mixed-cohort membership.
#689 open Which cohort a client session binds to, and whether that binding is verifiable. Single-session attachment identity, not fleet population.
#708 open Connected-but-namespaces-unattached, a client-side session attachment failure. Orthogonal axis.
#634 / #636 / #430 closed Web Console HTTP APIs and UI views. Not native MCP capabilities; an LLM under the native-only rail cannot call them, and #636 explicitly scopes to sessions/leases/locks/worktrees rather than fleet process inventory.
#657 closed Inventory of restart code paths, not running processes.

Canonical issue state

STATE: ready-for-author
WHO_IS_NEXT: author
NEXT_ACTION: Implement the read-only native fleet inventory capability on gitea-controller and gitea-reconciler per the acceptance criteria
NEXT_PROMPT:
Role: AUTHOR
Repo: prgs / Scaled-Tech-Consulting / Gitea-Tools
Issue: this issue

1. Native MCP only; gitea_whoami + gitea_resolve_task_capability(work_issue) on prgs-author; bind an issue-backed worktree under branches/.
2. Implement the authoritative read-only fleet inventory per the required behavior and structured result; fail closed when evidence is incomplete.
3. Expose it on gitea-controller and gitea-reconciler; strictly read-only, no restart, reconnect, lease mutation, or process termination.
4. Add regression tests for healthy, duplicate, missing, unexpected, mixed-revision, mixed-cohort, and incomplete-evidence cases.
5. Update canonical workflow documentation for how controller and reconciler gates consume the result.
6. Open a PR with Closes; stop; hand off to an independent reviewer.
Related: #900, #948, #686, #689, #708, #634, #636, #430, #887, #929 ## Problem The canonical workflow can independently prove revision parity through the five role namespaces, but no sanctioned native capability currently provides authoritative fleet-level evidence for: * Exactly one running server per configured PRGS profile * Missing servers * Duplicate servers * Process or worker identity * Client/cohort ownership * Mixed-client fleets * Mixed-revision fleets Consequently, an LLM cannot satisfy the strict five-process/single-cohort safety gate without relying on shell process inspection, cached JSON, source inspection, or caller-supplied evidence. Those substitutes are not authoritative workflow evidence. The nearest existing native surface is `gitea_assess_mcp_namespace_health`, and it is not a substitute: it is scoped to a single named namespace, and it accepts `process`, `probe_result`, and `registered_tools` **from the caller**. A capability whose inputs are supplied by the party it is meant to constrain cannot serve as authoritative fleet evidence. `gitea_get_runtime_context` and `gitea_assess_master_parity` are likewise per-process — each proves something about the server answering the call, and nothing about the other four. This gap was exposed during cross-LLM validation after PR #944. All five namespaces independently reported parity at `82d71b77028a7abd4f8ab4a4e4d89658a187f73d`, but strict fleet validation remained incomplete: five independent self-reports of the same revision do not prove that exactly five processes exist, that no sixth exists, or that all five belong to one client cohort. ## Required behavior Add an authoritative, read-only control-plane capability that reports: * Every configured PRGS fleet member * Expected namespace and profile * Assigned role * Repository binding * Whether that member is running * Stable process or worker identity * Runtime instance identity * Client/cohort identity * Startup revision * Current local and remote revision * Duplicate instances * Missing instances * Unexpected instances * Mixed-client/cohort membership * Mixed-revision membership * Overall completeness and health * Whether the fleet satisfies the exact-one-instance-per-profile invariant * Whether the result is sufficient for mutation-sensitive workflow gates The expected PRGS fleet is: * `gitea-author` / `prgs-author` * `gitea-controller` / `prgs-controller` * `gitea-reviewer` / `prgs-reviewer` * `gitea-merger` / `prgs-merger` * `gitea-reconciler` / `prgs-reconciler` ## Exposure and permissions The capability must: * Be exposed natively through `gitea-controller` * Be exposed natively through `gitea-reconciler` * Be strictly read-only * Require no shell or OS-process inspection by the caller * Return structured machine-readable evidence * Fail closed when complete authoritative evidence cannot be obtained * Distinguish `unknown` from `healthy` * Avoid treating profile configuration alone as proof that a server is running * Avoid treating parent-client status as proof of individual member health * Avoid inferring cohort identity solely from matching revisions ## Suggested structured result Include fields equivalent to: * `inventory_complete` * `configured_members` * `running_members` * `missing_members` * `duplicate_members` * `unexpected_members` * `mixed_revision` * `mixed_cohort` * `single_cohort` * `exactly_one_per_profile` * `mutation_gate_satisfied` * `blocked_reason` * Per-member: * namespace * profile * role * repository * process/worker identity * runtime identity * client/cohort identity * startup head * daemon-start head * local head * live-remote head * health Exact field names may follow existing repository conventions, but equivalent evidence is required. ## Acceptance criteria 1. A healthy five-server fleet reports all five expected members exactly once. 2. Two processes using the same profile are reported as a duplicate and fail the mutation gate. 3. A missing expected server is identified and fails the mutation gate. 4. An unexpected PRGS server is reported explicitly. 5. Members from different client cohorts are detected. 6. Members running different startup revisions are detected. 7. Matching Git revisions alone do not establish a single cohort. 8. Configuration without a live worker does not count as a running member. 9. Unknown or unavailable process evidence produces a fail-closed result. 10. Controller and reconciler return consistent inventory results for the same fleet snapshot. 11. The capability performs no restart, reconnect, lease mutation, issue mutation, or process termination. 12. Automated tests cover healthy, duplicate, missing, unexpected, mixed-revision, mixed-cohort, and incomplete-evidence cases. 13. Canonical workflow documentation explains how controller and reconciler gates consume the result. 14. An LLM can prove the exact-five-process/single-cohort invariant using only sanctioned native calls. ## Non-goals * Automatically terminating duplicate processes * Restarting or reconnecting the fleet * Clearing workflow leases * Repairing controller/reconciler role metadata * Reconstructing historical restart provenance * Cleaning the stale PR #906 author lease Those are separate workflows or issues. ## Duplicate verdict NOT a duplicate. Verified by reading the bodies and acceptance criteria of #900, #948, #686, #689, #708, #634, #636, #430, and #657. | Issue | State | Why it does not cover this | | --- | --- | --- | | #900 | open | Owns cohort **lifecycle supervision** — establishing a single eligible cohort, then draining and reaping superseded ones. That is a mutating supervisory transition. This issue is the strictly read-only evidence surface that such a supervisor, and every other gate, would consume. #900 also frames status per *cohort*; it does not enumerate configured-but-absent fleet members, does not cover repository binding per member, and does not define a `mutation_gate_satisfied` verdict. | | #948 | open | Defines the ownership *model* — client/session/generation identity and lease fencing — so that concurrent LLM clients are not falsely blocked. This issue defines the *observation* surface that reports what is actually running. #948 needs this evidence; it does not provide it. | | #686 | closed | Detection of manually launched duplicates by **provenance**, per profile, on the serving process. Never enumerates the fleet, never reports missing members, mixed-revision, or mixed-cohort membership. | | #689 | open | Which cohort a *client session binds to*, and whether that binding is verifiable. Single-session attachment identity, not fleet population. | | #708 | open | Connected-but-namespaces-unattached, a client-side session attachment failure. Orthogonal axis. | | #634 / #636 / #430 | closed | Web Console HTTP APIs and UI views. Not native MCP capabilities; an LLM under the native-only rail cannot call them, and #636 explicitly scopes to sessions/leases/locks/worktrees rather than fleet process inventory. | | #657 | closed | Inventory of restart *code paths*, not running processes. | ## Canonical issue state ```text STATE: ready-for-author WHO_IS_NEXT: author NEXT_ACTION: Implement the read-only native fleet inventory capability on gitea-controller and gitea-reconciler per the acceptance criteria NEXT_PROMPT: Role: AUTHOR Repo: prgs / Scaled-Tech-Consulting / Gitea-Tools Issue: this issue 1. Native MCP only; gitea_whoami + gitea_resolve_task_capability(work_issue) on prgs-author; bind an issue-backed worktree under branches/. 2. Implement the authoritative read-only fleet inventory per the required behavior and structured result; fail closed when evidence is incomplete. 3. Expose it on gitea-controller and gitea-reconciler; strictly read-only, no restart, reconnect, lease mutation, or process termination. 4. Add regression tests for healthy, duplicate, missing, unexpected, mixed-revision, mixed-cohort, and incomplete-evidence cases. 5. Update canonical workflow documentation for how controller and reconciler gates consume the result. 6. Open a PR with Closes; stop; hand off to an independent reviewer. ```
Author
Owner

Canonical Issue State

STATE:
in-progress

WHO_IS_NEXT:
author

NEXT_ACTION:
Implement the authoritative fleet inventory on branch feat/issue-949-native-fleet-inventory: a control-plane server-runtime registry written by each server process, a pure classification module, and a read-only gitea_assess_fleet_inventory tool, with regression tests for all fourteen acceptance criteria.

NEXT_PROMPT:

Role: AUTHOR
Repo: prgs / Scaled-Tech-Consulting / Gitea-Tools
Issue: #949
Branch: feat/issue-949-native-fleet-inventory
Worktree: branches/issue-949-fleet-inventory
Pinned base: 82d71b77028a7abd4f8ab4a4e4d89658a187f73d

1. Add the mcp_server_runtimes control-plane table plus register/heartbeat/list/stop accessors.
2. Add mcp_fleet_inventory.py with pure, deterministic classification of expected, running, missing, duplicate, unexpected, and stale members.
3. Register the process runtime row from the official entrypoint immediately after the native transport bind.
4. Expose gitea_assess_fleet_inventory as a strictly read-only tool gated on gitea.read, callable from every role namespace including gitea-controller and gitea-reconciler.
5. Add regression tests for each acceptance criterion, run the narrow suite then the full suite against the pinned base, and open one pull request that closes #949.

WHAT_HAPPENED:
Author claimed #949, bootstrapped worktree branches/issue-949-fleet-inventory on branch feat/issue-949-native-fleet-inventory from pinned base 82d71b7702, and surveyed the existing surfaces. No competing branch, worktree, pull request, lease, or assignee exists for this issue. The only active control-plane lease is the unrelated stale author lease on PR #906, which this work leaves untouched per the stated non-goals.

WHY:
Every existing surface is per-process. gitea_get_runtime_context and gitea_assess_master_parity describe only the server answering the call. gitea_assess_mcp_namespace_health accepts process, probe_result, and registered_tools from the caller, so it cannot constrain the caller. control_plane_db.sessions records allocator task sessions, not server processes: nothing in the control plane records that a given MCP server process exists at all. The fix is therefore a control-plane record each server writes about itself at native transport bind, which no caller can supply, plus a pure classifier that consumes it.

Implementation plan, mapped to the acceptance criteria:

# Acceptance criterion Code location Behavior Proving test
1 Healthy five-server fleet reports five members exactly once mcp_fleet_inventory.classify_fleet_inventory Expected roster of the five PRGS namespace/profile/role triples matched against live registry rows tests/test_mcp_fleet_inventory.py healthy case
2 Two processes on one profile are a duplicate and fail the gate same Two live rows for one profile emit a duplicate_members entry and clear exactly_one_per_profile duplicate case
3 Missing expected server identified and fails the gate same Expected member with no live row lands in missing_members missing case
4 Unexpected PRGS server reported explicitly same Live row whose profile is outside the expected roster lands in unexpected_members, never folded into duplicates unexpected case
5 Members from different client cohorts detected same mixed_cohort derived only from distinct cohort_id values mixed-cohort case
6 Members on different startup revisions detected same mixed_revision derived from distinct startup_head values mixed-revision case
7 Matching revisions alone do not establish a single cohort same single_cohort computed strictly from cohort_id; identical heads with unknown cohort stay unknown and fail closed revision-is-not-cohort case
8 Configuration without a live worker is not a running member same plus control_plane_db liveness fields A row is running only when its pid is alive and its heartbeat is inside the freshness window; configured-only members stay missing dead-pid and stale-heartbeat cases
9 Unknown or unavailable evidence produces a fail-closed result same Any unknown cohort, unreadable registry, or process observed without a registry row sets inventory_complete false and mutation_gate_satisfied false with a specific blocked_reason incomplete-evidence cases
10 Controller and reconciler agree for one snapshot gitea_mcp_server.gitea_assess_fleet_inventory Classification is a pure function of the snapshot; the answering namespace is reported as metadata only and never changes the verdict cross-namespace equality case
11 No restart, reconnect, lease, issue, or process mutation same Read-only path; no writes outside the caller's own heartbeat, no signals other than a liveness probe read-only assertion case
12 Automated tests for every listed scenario tests/test_mcp_fleet_inventory.py, tests/test_control_plane_db_server_runtimes.py, tests/test_fleet_inventory_tool.py Scenario-per-test coverage the suite itself
13 Workflow documentation explains gate consumption docs/mcp-fleet-inventory.md plus skills/llm-project-workflow/SKILL.md Documents evidence authority, field meanings, and how controller and reconciler gates consume the verdict documentation presence test
14 The invariant is provable from sanctioned native calls only gitea_assess_fleet_inventory plus task_capability_map One native call returns exactly_one_per_profile, single_cohort, and mutation_gate_satisfied with no shell inspection by the caller capability-map exposure case

Additional checks in scope because the classifier would otherwise report a healthy fleet on bad evidence: repository-binding mismatch per member, and role/profile mismatch between a member's namespace and its declared role. Both fail closed.

Native namespaces affected: the tool is registered once and gated on gitea.read, so it answers identically from gitea-author, gitea-controller, gitea-reviewer, gitea-merger, and gitea-reconciler. The two namespaces the issue names are covered by that superset.

Compatibility: the control-plane schema change is additive and idempotent, matching the existing migration convention; no existing table, tool signature, or result field changes. A server built before this change simply has no registry row, which the completeness check reports as incomplete evidence rather than as a healthy fleet.

Scope boundaries preserved: controller role metadata and capability routing stay with #950, synchronization and restart receipts stay with #951, and stale-lease inspection consistency stays with #952. This work records no historical restart evidence and repairs no lease.

RELATED_PRS:
None yet. One pull request will be opened from feat/issue-949-native-fleet-inventory against master.

BLOCKERS:
No blocker.

VALIDATION:
Pending. Planned evidence is the new focused test modules, then the repository full suite at the head compared against the same suite at pinned base 82d71b7702, comparing failing test identifiers rather than counts.

LAST_UPDATED_BY:
author (jcwalker3, prgs-author)

## Canonical Issue State STATE: in-progress WHO_IS_NEXT: author NEXT_ACTION: Implement the authoritative fleet inventory on branch feat/issue-949-native-fleet-inventory: a control-plane server-runtime registry written by each server process, a pure classification module, and a read-only gitea_assess_fleet_inventory tool, with regression tests for all fourteen acceptance criteria. NEXT_PROMPT: ```text Role: AUTHOR Repo: prgs / Scaled-Tech-Consulting / Gitea-Tools Issue: #949 Branch: feat/issue-949-native-fleet-inventory Worktree: branches/issue-949-fleet-inventory Pinned base: 82d71b77028a7abd4f8ab4a4e4d89658a187f73d 1. Add the mcp_server_runtimes control-plane table plus register/heartbeat/list/stop accessors. 2. Add mcp_fleet_inventory.py with pure, deterministic classification of expected, running, missing, duplicate, unexpected, and stale members. 3. Register the process runtime row from the official entrypoint immediately after the native transport bind. 4. Expose gitea_assess_fleet_inventory as a strictly read-only tool gated on gitea.read, callable from every role namespace including gitea-controller and gitea-reconciler. 5. Add regression tests for each acceptance criterion, run the narrow suite then the full suite against the pinned base, and open one pull request that closes #949. ``` WHAT_HAPPENED: Author claimed #949, bootstrapped worktree branches/issue-949-fleet-inventory on branch feat/issue-949-native-fleet-inventory from pinned base 82d71b77028a7abd4f8ab4a4e4d89658a187f73d, and surveyed the existing surfaces. No competing branch, worktree, pull request, lease, or assignee exists for this issue. The only active control-plane lease is the unrelated stale author lease on PR #906, which this work leaves untouched per the stated non-goals. WHY: Every existing surface is per-process. gitea_get_runtime_context and gitea_assess_master_parity describe only the server answering the call. gitea_assess_mcp_namespace_health accepts process, probe_result, and registered_tools from the caller, so it cannot constrain the caller. control_plane_db.sessions records allocator task sessions, not server processes: nothing in the control plane records that a given MCP server process exists at all. The fix is therefore a control-plane record each server writes about itself at native transport bind, which no caller can supply, plus a pure classifier that consumes it. Implementation plan, mapped to the acceptance criteria: | # | Acceptance criterion | Code location | Behavior | Proving test | | --- | --- | --- | --- | --- | | 1 | Healthy five-server fleet reports five members exactly once | mcp_fleet_inventory.classify_fleet_inventory | Expected roster of the five PRGS namespace/profile/role triples matched against live registry rows | tests/test_mcp_fleet_inventory.py healthy case | | 2 | Two processes on one profile are a duplicate and fail the gate | same | Two live rows for one profile emit a duplicate_members entry and clear exactly_one_per_profile | duplicate case | | 3 | Missing expected server identified and fails the gate | same | Expected member with no live row lands in missing_members | missing case | | 4 | Unexpected PRGS server reported explicitly | same | Live row whose profile is outside the expected roster lands in unexpected_members, never folded into duplicates | unexpected case | | 5 | Members from different client cohorts detected | same | mixed_cohort derived only from distinct cohort_id values | mixed-cohort case | | 6 | Members on different startup revisions detected | same | mixed_revision derived from distinct startup_head values | mixed-revision case | | 7 | Matching revisions alone do not establish a single cohort | same | single_cohort computed strictly from cohort_id; identical heads with unknown cohort stay unknown and fail closed | revision-is-not-cohort case | | 8 | Configuration without a live worker is not a running member | same plus control_plane_db liveness fields | A row is running only when its pid is alive and its heartbeat is inside the freshness window; configured-only members stay missing | dead-pid and stale-heartbeat cases | | 9 | Unknown or unavailable evidence produces a fail-closed result | same | Any unknown cohort, unreadable registry, or process observed without a registry row sets inventory_complete false and mutation_gate_satisfied false with a specific blocked_reason | incomplete-evidence cases | | 10 | Controller and reconciler agree for one snapshot | gitea_mcp_server.gitea_assess_fleet_inventory | Classification is a pure function of the snapshot; the answering namespace is reported as metadata only and never changes the verdict | cross-namespace equality case | | 11 | No restart, reconnect, lease, issue, or process mutation | same | Read-only path; no writes outside the caller's own heartbeat, no signals other than a liveness probe | read-only assertion case | | 12 | Automated tests for every listed scenario | tests/test_mcp_fleet_inventory.py, tests/test_control_plane_db_server_runtimes.py, tests/test_fleet_inventory_tool.py | Scenario-per-test coverage | the suite itself | | 13 | Workflow documentation explains gate consumption | docs/mcp-fleet-inventory.md plus skills/llm-project-workflow/SKILL.md | Documents evidence authority, field meanings, and how controller and reconciler gates consume the verdict | documentation presence test | | 14 | The invariant is provable from sanctioned native calls only | gitea_assess_fleet_inventory plus task_capability_map | One native call returns exactly_one_per_profile, single_cohort, and mutation_gate_satisfied with no shell inspection by the caller | capability-map exposure case | Additional checks in scope because the classifier would otherwise report a healthy fleet on bad evidence: repository-binding mismatch per member, and role/profile mismatch between a member's namespace and its declared role. Both fail closed. Native namespaces affected: the tool is registered once and gated on gitea.read, so it answers identically from gitea-author, gitea-controller, gitea-reviewer, gitea-merger, and gitea-reconciler. The two namespaces the issue names are covered by that superset. Compatibility: the control-plane schema change is additive and idempotent, matching the existing migration convention; no existing table, tool signature, or result field changes. A server built before this change simply has no registry row, which the completeness check reports as incomplete evidence rather than as a healthy fleet. Scope boundaries preserved: controller role metadata and capability routing stay with #950, synchronization and restart receipts stay with #951, and stale-lease inspection consistency stays with #952. This work records no historical restart evidence and repairs no lease. RELATED_PRS: None yet. One pull request will be opened from feat/issue-949-native-fleet-inventory against master. BLOCKERS: No blocker. VALIDATION: Pending. Planned evidence is the new focused test modules, then the repository full suite at the head compared against the same suite at pinned base 82d71b77028a7abd4f8ab4a4e4d89658a187f73d, comparing failing test identifiers rather than counts. LAST_UPDATED_BY: author (jcwalker3, prgs-author)
Author
Owner

Canonical Issue State

STATE:
blocked

WHO_IS_NEXT:
author

NEXT_ACTION:
Restore a create_pr-eligible durable issue lock for #949 so the already-pushed head 92615f474b can be opened as a pull request; the implementation itself is complete and published.

NEXT_PROMPT:

Role: AUTHOR
Repo: prgs / Scaled-Tech-Consulting / Gitea-Tools
Issue: #949
Branch: feat/issue-949-native-fleet-inventory (published)
Head: 92615f474bf6652d4e9ea59af7fd0dba03b56544
Base: master at 82d71b77028a7abd4f8ab4a4e4d89658a187f73d
Worktree: branches/issue-949-fleet-inventory (clean, on the branch, head equals remote head)

The implementation, tests, and push are finished. Only the pull request remains.
Obtain a durable issue lock that carries sanctioned lock_provenance and work_lease
for issue #949 on this exact branch and worktree, then call gitea_create_pr with
this worktree_path. Do not hand-edit the lock file and do not move the branch
pointer to satisfy a gate. If no sanctioned tool can mint that lock while the
branch already carries the committed work, treat the bootstrap defect described
below as the blocking dependency and report it rather than working around it.

WHAT_HAPPENED:
The full #949 contract is implemented, committed, and published. Head 92615f474b on feat/issue-949-native-fleet-inventory adds gitea_assess_fleet_inventory, the mcp_server_runtimes control-plane registry that each server writes about itself at native transport bind, the pure classification module, the capability-map entries, and the documentation. 118 new tests pass. The repository full suite at this head is 28 failed / 5810 passed against 28 failed / 5692 passed at the pinned base, with byte-identical failing-test-identifier sets, so there are zero regressions and the passing delta is exactly the new tests.

The pull request could not be created. gitea_create_pr fails closed with the #447 issue-lock provenance guard: the durable lock file lacks sanctioned lock_provenance.

WHY:
The durable lock for this issue was created by gitea_bootstrap_author_issue_worktree, whose own reported next action was to proceed directly to implementation. That tool writes a lock shape no downstream author tool accepts, and the shape cannot be upgraded once implementation work is committed:

  1. gitea_create_pr requires lock_provenance plus work_lease. Only gitea_lock_issue writes those two fields.
  2. gitea_heartbeat_issue_lock refuses the bootstrap lock because issue_lock_store reads the claimant from work_lease.claimant while the bootstrap wrote it at the lock top level.
  3. gitea_lock_issue now refuses because the worktree is no longer base-equivalent to master, which is correct: the branch carries the committed work.
  4. The exact-owner renewal waiver that exists for precisely this shape of branch requires an expired lease, and is_lease_expired returns false when no expires_at is recorded, so a bootstrap lock is treated as never expiring and the waiver never engages.

Each of the four is fail-closed and correct in isolation. Together they leave a bootstrapped author task with no sanctioned route to a pull request. This was verified by reading issue_lock_provenance.assess_lock_file_for_create_pr, issue_lock_store.is_lease_expired, issue_lock_worktree.assess_issue_lock_worktree, and issue_lock_renewal, not inferred from the error text.

No workaround was attempted. The lock file was not edited by hand, the branch pointer was not moved to satisfy the base-equivalence check, and no fallback transport was used. This gap is a separate defect from #949 and should be filed and fixed on its own terms rather than absorbed here.

RELATED_PRS:
None. No pull request exists for #949 and none was created. The implementation is published on branch feat/issue-949-native-fleet-inventory at 92615f474b, awaiting a pull request.

BLOCKERS:
Blocker classification: environment/tooling blocker. gitea_create_pr requires a durable issue lock carrying sanctioned lock_provenance and work_lease, which only gitea_lock_issue writes. This remains blocked until a sanctioned tool can mint that lock for a branch that already carries committed work, or until the gitea_bootstrap_author_issue_worktree lock shape is corrected to record lock_provenance and work_lease at creation time.

VALIDATION:
Narrow suites: tests/test_mcp_fleet_inventory.py, tests/test_control_plane_db_server_runtimes.py, tests/test_fleet_inventory_tool.py — 118 passed, 0 failed, exit 0. Adjacent suites including tests/test_control_plane_db.py, tests/test_task_capability_role_invariants.py, tests/test_mcp_namespace_health.py, tests/test_issue_686_manual_mcp_provenance.py, tests/test_restart_coordinator.py, tests/test_webui_inventory.py, tests/test_issue_695_native_transport_quarantine.py — 290 passed, 84 subtests passed, 1 failed, that one being a failure proven present at the pinned base. Full suite head: 28 failed, 5810 passed, 6 skipped, 1042 subtests. Full suite at base 82d71b7702: 28 failed, 5692 passed, 6 skipped, 1042 subtests. Sorted failing identifier sets compared with diff: identical, zero regressions. Control checkout verified clean on master throughout; the temporary base worktree used for the comparison was removed.

LAST_UPDATED_BY:
author (jcwalker3, prgs-author)

## Canonical Issue State STATE: blocked WHO_IS_NEXT: author NEXT_ACTION: Restore a create_pr-eligible durable issue lock for #949 so the already-pushed head 92615f474bf6652d4e9ea59af7fd0dba03b56544 can be opened as a pull request; the implementation itself is complete and published. NEXT_PROMPT: ```text Role: AUTHOR Repo: prgs / Scaled-Tech-Consulting / Gitea-Tools Issue: #949 Branch: feat/issue-949-native-fleet-inventory (published) Head: 92615f474bf6652d4e9ea59af7fd0dba03b56544 Base: master at 82d71b77028a7abd4f8ab4a4e4d89658a187f73d Worktree: branches/issue-949-fleet-inventory (clean, on the branch, head equals remote head) The implementation, tests, and push are finished. Only the pull request remains. Obtain a durable issue lock that carries sanctioned lock_provenance and work_lease for issue #949 on this exact branch and worktree, then call gitea_create_pr with this worktree_path. Do not hand-edit the lock file and do not move the branch pointer to satisfy a gate. If no sanctioned tool can mint that lock while the branch already carries the committed work, treat the bootstrap defect described below as the blocking dependency and report it rather than working around it. ``` WHAT_HAPPENED: The full #949 contract is implemented, committed, and published. Head 92615f474bf6652d4e9ea59af7fd0dba03b56544 on feat/issue-949-native-fleet-inventory adds gitea_assess_fleet_inventory, the mcp_server_runtimes control-plane registry that each server writes about itself at native transport bind, the pure classification module, the capability-map entries, and the documentation. 118 new tests pass. The repository full suite at this head is 28 failed / 5810 passed against 28 failed / 5692 passed at the pinned base, with byte-identical failing-test-identifier sets, so there are zero regressions and the passing delta is exactly the new tests. The pull request could not be created. gitea_create_pr fails closed with the #447 issue-lock provenance guard: the durable lock file lacks sanctioned lock_provenance. WHY: The durable lock for this issue was created by gitea_bootstrap_author_issue_worktree, whose own reported next action was to proceed directly to implementation. That tool writes a lock shape no downstream author tool accepts, and the shape cannot be upgraded once implementation work is committed: 1. gitea_create_pr requires lock_provenance plus work_lease. Only gitea_lock_issue writes those two fields. 2. gitea_heartbeat_issue_lock refuses the bootstrap lock because issue_lock_store reads the claimant from work_lease.claimant while the bootstrap wrote it at the lock top level. 3. gitea_lock_issue now refuses because the worktree is no longer base-equivalent to master, which is correct: the branch carries the committed work. 4. The exact-owner renewal waiver that exists for precisely this shape of branch requires an expired lease, and is_lease_expired returns false when no expires_at is recorded, so a bootstrap lock is treated as never expiring and the waiver never engages. Each of the four is fail-closed and correct in isolation. Together they leave a bootstrapped author task with no sanctioned route to a pull request. This was verified by reading issue_lock_provenance.assess_lock_file_for_create_pr, issue_lock_store.is_lease_expired, issue_lock_worktree.assess_issue_lock_worktree, and issue_lock_renewal, not inferred from the error text. No workaround was attempted. The lock file was not edited by hand, the branch pointer was not moved to satisfy the base-equivalence check, and no fallback transport was used. This gap is a separate defect from #949 and should be filed and fixed on its own terms rather than absorbed here. RELATED_PRS: None. No pull request exists for #949 and none was created. The implementation is published on branch feat/issue-949-native-fleet-inventory at 92615f474bf6652d4e9ea59af7fd0dba03b56544, awaiting a pull request. BLOCKERS: Blocker classification: environment/tooling blocker. gitea_create_pr requires a durable issue lock carrying sanctioned lock_provenance and work_lease, which only gitea_lock_issue writes. This remains blocked until a sanctioned tool can mint that lock for a branch that already carries committed work, or until the gitea_bootstrap_author_issue_worktree lock shape is corrected to record lock_provenance and work_lease at creation time. VALIDATION: Narrow suites: tests/test_mcp_fleet_inventory.py, tests/test_control_plane_db_server_runtimes.py, tests/test_fleet_inventory_tool.py — 118 passed, 0 failed, exit 0. Adjacent suites including tests/test_control_plane_db.py, tests/test_task_capability_role_invariants.py, tests/test_mcp_namespace_health.py, tests/test_issue_686_manual_mcp_provenance.py, tests/test_restart_coordinator.py, tests/test_webui_inventory.py, tests/test_issue_695_native_transport_quarantine.py — 290 passed, 84 subtests passed, 1 failed, that one being a failure proven present at the pinned base. Full suite head: 28 failed, 5810 passed, 6 skipped, 1042 subtests. Full suite at base 82d71b77028a7abd4f8ab4a4e4d89658a187f73d: 28 failed, 5692 passed, 6 skipped, 1042 subtests. Sorted failing identifier sets compared with diff: identical, zero regressions. Control checkout verified clean on master throughout; the temporary base worktree used for the comparison was removed. LAST_UPDATED_BY: author (jcwalker3, prgs-author)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#949