feat(devkit): add resumable workspace automation and UI review tooling
Verified with the coordinated workspace changes by devkit full run 2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed). This shared UI pass does not mark the individual module reviews complete.
This commit is contained in:
Executable
+300
@@ -0,0 +1,300 @@
|
||||
# Developer command suite
|
||||
|
||||
`./devkit` is the maintained entry point for repeated development work. It uses
|
||||
ordinary local programs, not an AI service. People, CI and coding agents use the
|
||||
same commands and results. It composes existing GovOPlaN checks and release
|
||||
services rather than defining parallel product rules.
|
||||
|
||||
## Start here
|
||||
|
||||
From the Meta checkout:
|
||||
|
||||
```sh
|
||||
./devkit commands
|
||||
./devkit doctor --repo core
|
||||
./devkit context --changed
|
||||
./devkit check --profile quick --repo campaign --dry-run
|
||||
./devkit coverage --profile ui --repo campaign
|
||||
./devkit check --profile quick --repo campaign
|
||||
./devkit latest
|
||||
./devkit review campaign
|
||||
```
|
||||
|
||||
Use `--help` on each command. `--json` (or `--format json`) returns structured
|
||||
results; the default summary intentionally omits full logs and source contents.
|
||||
Global options work before or after the command. `--workspace-root` is the parent
|
||||
directory of the registered repositories, not the Core checkout. Unknown
|
||||
repository filters fail rather than silently widening/narrowing scope.
|
||||
|
||||
The launcher prefers Meta's `.venv/bin/python`, or the explicit `PYTHON`
|
||||
executable, with `python3` as the no-venv fallback. Checks resolve `NODE` and
|
||||
`NPM` from explicit environment settings or `PATH`; no username-specific Node
|
||||
installation path is required. Doctor reports repairs but never installs
|
||||
packages, changes configuration, kills an occupied port or starts a server.
|
||||
|
||||
## Command contracts
|
||||
|
||||
| Command | Purpose | Effects |
|
||||
| --- | --- | --- |
|
||||
| `context [--changed] [--repo NAME]` | Offline Git state, instruction/documentation paths and review links | Reads only; upstream counts are not freshly fetched |
|
||||
| `doctor [--repo NAME] [--profile PROFILE]` | Interpreter, dependency, browser and test-resource preflight | Reads only; repair suggestions are not executed |
|
||||
| `check --profile PROFILE` | Registered checks with explanations, bounded logs and a receipt | Runs trusted tests/builds; `--dry-run` only plans |
|
||||
| `coverage --profile PROFILE [--repo NAME]` | Declared suite dispositions and explicit coverage limits | Reads only; not execution evidence |
|
||||
| `runs [--limit 10] [--before RUN]`, `latest` | Discover recent runs without looking up state-directory paths | Reads only; malformed latest evidence is not replaced with an older pass |
|
||||
| `status RUN`, `summary RUN` | Stage counts and failure log locations | Reads only |
|
||||
| `logs RUN --stage STAGE --tail 40 [--final-only]` | Provisional live output or a hash-verified final stage log | Reads only; provisional output never proves a pass |
|
||||
| `resume RUN` | Replan the previous check selection and reuse eligible successful stages | Runs remaining checks, creating a new receipt |
|
||||
| `recover RUN [--apply]` | Recover an abandoned check-run record after acquiring its OS lock | No test replay; apply changes only the local record |
|
||||
| `review MODULE` | Source inventory, principle revision, issue links, check plan and walkthrough checklist | Does not perform or complete a module review |
|
||||
| `docs audit [--repo NAME] [--changed]` | Owning manifest/help/translation checks and visible-label gaps | Local checks only; never translates or edits content |
|
||||
| `issues note …` | Preview and explicitly append deduplicated evidence | Remote comment creation only with `--apply` |
|
||||
| `release …` | Existing durable release planning/execution/recovery | Explicit `--apply`, request IDs and existing step confirmations |
|
||||
| `git …` | Frozen, explicit-path maintenance commit and exact branch push | Preview first; mutations require `--apply`; no stage-all, force or tags |
|
||||
|
||||
Git maintenance is a separate, selected-path workflow; see
|
||||
[Git maintenance](DEVKIT_MAINTENANCE.md). Issue notes and module bundles are
|
||||
documented in [Evidence and review commands](DEVKIT_EVIDENCE.md). The complete
|
||||
release command syntax, limitations and examples are in
|
||||
[Headless release operations](DEVKIT_RELEASE.md).
|
||||
|
||||
## Check profiles and their limits
|
||||
|
||||
- `quick`: existing manifest/interface invariants, import/help guards and selected
|
||||
module source/structure tests. It does not compile component suites or run the
|
||||
full browser matrix.
|
||||
- `ui`: quick/source contracts plus the shared Core component batch, compiled
|
||||
once in an isolated directory. This is not a full visual/usability review.
|
||||
- `backend`: existing manifest/interface checks and selected repository backend
|
||||
test suites. Shared backend-test state is serialized.
|
||||
- `full`: the canonical `tools/checks/check-focused.sh`, including optional-module
|
||||
build permutations and integrated browser conformance. Repository filters do
|
||||
not reduce this required cross-module completion gate.
|
||||
|
||||
`--changed` includes uncommitted work, commits ahead of the locally cached
|
||||
upstream and repositories with commits but no configured upstream.
|
||||
Missing/unreadable repositories remain visible as errors. For the
|
||||
GovOPlaN profiles, Core or Meta changes conservatively select every registered
|
||||
consumer; other changes expand through declared interface consumers. This is
|
||||
not complete semantic dependency analysis. Use the full gate for cross-module
|
||||
completion as required by `AGENTS.md`.
|
||||
|
||||
The module catalog reuses declared direct-Node tests and established structural
|
||||
script names. Arbitrary shell chains are not guessed or rewritten. Unsupported
|
||||
declared scripts remain visible with a reason; only exact known Core component
|
||||
aliases are covered by the shared component runner. Invalid package metadata is
|
||||
an error, not an empty test inventory.
|
||||
|
||||
`coverage` inventories declared suites as `planned`, `covered_elsewhere`,
|
||||
`excluded` or `unsupported`, naming their covering stage where applicable. The
|
||||
same inventory accompanies check dry runs and saved receipts. **Full means the
|
||||
canonical focused gate, not every test in every package.** Its inventory follows
|
||||
explicit canonical commands conservatively; it does not infer arbitrary nested
|
||||
shell commands or npm hooks. Add new deterministic checks to the owning
|
||||
package/canonical gate and the catalog's tests as appropriate. Coverage planning
|
||||
does not execute the scripts it inventories or assert that they passed. See
|
||||
[Coverage and validation details](DEVKIT_COVERAGE.md) for examples and boundaries.
|
||||
|
||||
Independent checks can run with `--jobs 1..8` (default 2). Shared resources use
|
||||
OS locks for the current user, including across runs with different evidence
|
||||
directories. Browser/port locks are shared across that user's workspaces. Full runs
|
||||
reserve the shared WebUI/backend/browser resources. Standalone Core component
|
||||
commands use isolated temporary build directories; `npm run test:components --
|
||||
page-layout documentation-help` compiles once for that batch. Managed temporary
|
||||
output is cleaned; no user worktree is reset or restored.
|
||||
|
||||
## Evidence, interruption and safe reuse
|
||||
|
||||
A check announces its run ID and saves a `preparing` receipt before source and
|
||||
environment fingerprinting. Progress on stderr shows elapsed time, stage counts
|
||||
and active stages; it also distinguishes `checking` from final snapshot
|
||||
verification (`finalizing`). `--json` keeps stdout as one final JSON result and
|
||||
emits structured progress events on stderr; `--quiet` suppresses these events.
|
||||
Use `latest`, `status RUN` and `logs RUN --stage STAGE` from another terminal
|
||||
while a check runs. Counts are stages, not a guessed percentage of test effort.
|
||||
|
||||
Live snapshots are private, bounded, explicitly provisional and not accepted for
|
||||
reuse or issue evidence. A completed stage log can be hash-verified before the
|
||||
whole run finishes, but only the final run snapshot verifies the overall result.
|
||||
`--final-only` rejects unfinished logs. Run history supports cursor pagination
|
||||
(up to 100 rows per page), bounds directory scanning, and never deletes evidence.
|
||||
An explicitly selected `--project` also filters run discovery by project file.
|
||||
|
||||
Default records are private below
|
||||
`$XDG_STATE_HOME/govoplan/devkit/workspace-<identity>/runs/<run>/` (fallback
|
||||
`~/.local/state`). `--state-dir` selects another base; workspace scoping still
|
||||
applies. Status changes are atomically persisted with restrictive file modes;
|
||||
symlinked evidence paths are rejected. A record contains source/environment/plan
|
||||
fingerprints, stage reasons, status, exit code, timing and log identities.
|
||||
|
||||
Stages distinguish pending, running, passed, failed, timed out, blocked, skipped,
|
||||
stale and interrupted. A skipped or unexecuted stage is never a pass. A source or
|
||||
environment change during verification marks the overall result **stale**, even
|
||||
when individual subprocesses returned zero. Such a result is not passing evidence.
|
||||
|
||||
`resume RUN` replans the current selection and compares each phase independently.
|
||||
Matching **verified checkpoints** are reused; changed/new phases and phases
|
||||
without a checkpoint run again. A checkpoint binds the exact command and working
|
||||
directory, declared repository inputs, transitive data-dependency identities,
|
||||
devkit implementation/schema, tool/dependency metadata and environment. Logs must
|
||||
retain their recorded hashes; a missing or changed cached log fails closed.
|
||||
The summary reports how many checkpoints were reused; JSON records `input_scope`,
|
||||
`cache_key`, `checkpoint_verified`, `reused_from` and the reuse/rerun reason.
|
||||
|
||||
Each checkpoint is saved while its resource locks remain held, only after exit
|
||||
zero, a verified final log, and matching before/after inputs and environment.
|
||||
A phase that changes its own inputs is stale, even if another phase later restores
|
||||
those bytes. Final verification rechecks all passed/reused phases against current
|
||||
inputs before the new aggregate can pass. An interrupted, failed or stale run may
|
||||
donate an independently verified matching checkpoint, but never becomes passing
|
||||
evidence itself. Old receipts remain readable: legacy monolithic logs and receipts
|
||||
without these checkpoints cannot be retroactively split or reused as verified phases.
|
||||
|
||||
After a hard crash, `recover RUN` shows the abandoned owner and guidance. Inspect the
|
||||
recorded commands and manually stop any surviving test/build processes before
|
||||
using `recover RUN --apply --confirm-processes-stopped`. The command verifies the
|
||||
run lock is free before marking the record interrupted. A free parent lock alone
|
||||
does **not** prove children stopped after a hard kill. Recovery does not replay
|
||||
processes or infer that a partially completed operation succeeded.
|
||||
|
||||
### Canonical phases and narrower inputs
|
||||
|
||||
The full gate has seven explicit phases: `preflight`, `tooling`, `backend`,
|
||||
`core-ui`, `module-builds`, `browser`, and `module-ui`. Devkit stage IDs are
|
||||
`focused.<phase>`. The authoritative Bash bodies remain in
|
||||
`tools/checks/check-focused.sh`; the bounded metadata in
|
||||
`tools/checks/focused-phases.json` names their order and resources. The direct
|
||||
shell command still runs every phase in the original fail-fast order. For inspection:
|
||||
|
||||
```sh
|
||||
tools/checks/check-focused.sh --list-phases
|
||||
./devkit check --profile full --jobs 1
|
||||
./devkit resume RUN_ID --jobs 1
|
||||
```
|
||||
|
||||
For example, a browser failure no longer forces successful backend/build phases
|
||||
to run again if their inputs still match. Standalone `--phase browser` is available
|
||||
for diagnostics, but is not a substitute for the complete gate or its receipt.
|
||||
|
||||
Scopes are whole repositories, not inferred file globs. A trusted check may declare
|
||||
`"inputs": {"repos": ["canonical-repository-name"]}`; no declaration means the whole
|
||||
registered workspace. Dry runs show that boundary. Native source-discovery checks
|
||||
retain broad scopes where cross-module dependencies cannot safely be narrowed.
|
||||
Changes outside an explicit scope do not invalidate it, but tool/environment
|
||||
changes remain conservatively global. Scope declarations are a correctness
|
||||
contract: include every source repository that the check reads, not only its cwd.
|
||||
Native discovery ownership/presence is rechecked at phase boundaries, so a newly
|
||||
appearing module or WebUI directory invalidates the in-flight environment identity.
|
||||
Unregistered native sibling sources disable reuse with an explicit coverage note;
|
||||
register them before relying on cached results. Ordinary generated files inside
|
||||
existing source directories do not change this directory-shape identity.
|
||||
|
||||
Every snapshot rereads repository presence, HEAD, index/flags and tracked plus
|
||||
non-ignored untracked membership, including assume-unchanged files. File hashes
|
||||
use a bounded **in-process-only** memo with file identity/ctime/mtime/size checks
|
||||
before and after an open file descriptor; no persistent mtime cache is trusted.
|
||||
In-repository regular-file symlink targets are included; escaping or directory
|
||||
symlinks fail closed. Ignored files, undeclared external data and service state
|
||||
are not source evidence and need explicit verification.
|
||||
|
||||
Environment probes stream bounded regular-file contents and reject concurrent
|
||||
replacement, growth and symlink retargeting. Stable venv executable and dependency
|
||||
directory symlinks remain supported without changing the executable path; absent
|
||||
optional metadata remains optional. Environment identities are not persistently
|
||||
cached and remain deliberately global.
|
||||
|
||||
These checkpoints cache **verification results, not output artifacts**. The full
|
||||
gate's phases were audited so later phases do not require an earlier phase's
|
||||
retained build output. A custom setup/build that produces files consumed later
|
||||
must declare `"reuse": "never"`; its consumers must use `deps`. Until output
|
||||
manifests/restoration exist, do not assume a cached successful build recreates
|
||||
deleted ignored artifacts.
|
||||
|
||||
Check output is drained without unbounded memory growth. Stored output retains
|
||||
the beginning and actual final tail within an 8 MiB bound per stage, with a small
|
||||
truncation marker and an omitted-byte count; output truncation does not change
|
||||
the subprocess exit result. `logs` exposes at most 200 lines and 16 KiB. Live
|
||||
snapshots retain at most 64 KiB after redaction. Incomplete live lines and cut
|
||||
retention-boundary lines are withheld to avoid exposing fragments of secrets;
|
||||
very long single-line output may therefore be absent from live views.
|
||||
Known environment credentials and common authorization patterns are
|
||||
redacted as display hygiene. **Never pass credentials as check arguments or print
|
||||
them from tests.** Redaction is not a general secret-classification guarantee.
|
||||
JSON command output is a redacted presentation; the receipt file at the reported
|
||||
path is the canonical local record. Cancellation and deadlines terminate the
|
||||
owned process group; a parent leaving running descendants does not pass.
|
||||
During source/environment fingerprinting, cancellation is observed between
|
||||
probes; the current bounded probe may finish first. Cancellation during final
|
||||
verification is recorded as interrupted, never as a passing run.
|
||||
Deliberately detached processes are outside that group: this is not a sandbox.
|
||||
|
||||
These local hash-bound records detect accidental changes; they are not signed
|
||||
attestations, a security certification, proof of complete test coverage, or
|
||||
permission to publish. External service state and undeclared dependencies still
|
||||
need explicit verification. No automated result closes a Gitea issue or marks a
|
||||
module reviewed. Detailed logs remain local unless deliberately shared. Old run
|
||||
directories are not automatically deleted; review retention before removing any
|
||||
evidence referenced by an issue.
|
||||
|
||||
## Reuse in another project
|
||||
|
||||
The runner, repository context, doctor and evidence primitives are usable with an
|
||||
explicit local JSON project manifest. The example at
|
||||
`tools/devkit/examples/project.json` registers ordinary Python tests and a Git
|
||||
whitespace check; the format is described by
|
||||
`tools/devkit/project.schema.json`.
|
||||
|
||||
```sh
|
||||
/path/to/govoplan/devkit --workspace-root /path/to/project \
|
||||
--project /path/to/project/devkit-project.json check --profile quick --dry-run
|
||||
```
|
||||
|
||||
Repository paths and check working directories must remain inside the selected
|
||||
workspace. Check arguments are arrays, not shell-evaluated strings. The supported
|
||||
tool placeholders are `{python}`, `{node}`, `{npm}` and `{workspace}`. Check
|
||||
dependencies form an acyclic graph; resources serialize incompatible tasks.
|
||||
`deps` declares actual data dependencies: consumers rerun whenever a dependency
|
||||
runs again. `after` declares only fail-fast execution ordering: a failed predecessor
|
||||
skips the follower, but a successfully rerun independent predecessor does not
|
||||
invalidate the follower's inputs. Both edge types are validated together for
|
||||
cycles, missing IDs and overlap. `reuse` is `verified` by default or `never` for
|
||||
setup/output-producing checks. Repository `inputs` names are canonical, unique,
|
||||
nonempty and validated even for unselected checks.
|
||||
The complete custom manifest is validated, including unselected checks/profiles:
|
||||
unknown fields, misspellings, invalid bounds/types and unresolved dependencies
|
||||
fail early. This prevents silently ignored resource or timeout declarations.
|
||||
|
||||
Portable `doctor` always requires its Python runtime, plus tools needed by the
|
||||
selected checks and their dependencies and tools explicitly configured in the
|
||||
manifest. Without `--profile`, all declared profiles are considered. Unused
|
||||
Node/npm are marked `not_required` and do not block Python-only projects. Declare
|
||||
indirect tool dependencies explicitly: arbitrary script contents are not
|
||||
analyzed. Native GovOPlaN retains its Python/Node/npm requirements.
|
||||
Only use project manifests and scripts you trust: running a registered test is
|
||||
ordinary code execution, not a sandbox. Platform-specific release and Docs
|
||||
commands reject generic project manifests; put another project's checks in its
|
||||
own profiles rather than pretending its release policy is GovOPlaN's.
|
||||
|
||||
The initial runtime targets POSIX environments with Python 3.11+ and OS advisory
|
||||
locks; it does not claim Windows support. No globally installed service or Codex
|
||||
plugin is needed. Keep this implementation versioned and reuse it; avoid copying
|
||||
diverging helper implementations into each project.
|
||||
|
||||
Devkit is not a complete build system: it does not infer semantic dependencies,
|
||||
restore output artifacts, sandbox checks, or share signed remote caches. Repository
|
||||
scopes intentionally stop short of file/glob narrowing; environment identities
|
||||
remain global. These are explicit boundaries, not claims of exhaustive coverage.
|
||||
|
||||
## Development and conformance
|
||||
|
||||
```sh
|
||||
./.venv/bin/python -m pytest -q tests/test_devkit_*.py
|
||||
./.venv/bin/python -m pytest -q tests/test_focused_phases.py
|
||||
node --test tests/test-devkit-display-labels.mjs
|
||||
./.venv/bin/python -m unittest tests.test_documentation_structure
|
||||
```
|
||||
|
||||
The focused gate includes the Python command-suite regression tests. Fixture
|
||||
tests use temporary repositories/processes and mocked issue/release transports;
|
||||
they do not commit user work, send mail, publish artifacts or change live issues.
|
||||
Documentation is part of each command change: update `--help`, this guide and the
|
||||
corresponding targeted contract tests together.
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
# Check configuration and suite coverage
|
||||
|
||||
`devkit coverage` explains what a selected plan intends to run. It does not run
|
||||
tests, start servers, or report successful verification:
|
||||
|
||||
```sh
|
||||
./devkit coverage --profile quick --repo campaign
|
||||
./devkit coverage --profile ui --repo portal --json
|
||||
./devkit coverage --profile full --json
|
||||
./devkit --project /path/to/project.json coverage --profile quick
|
||||
```
|
||||
|
||||
The inventory accounts for declared `test` and `test:*` scripts in each
|
||||
registered repository's root and `webui/package.json`, discovered UI structural
|
||||
checks, and explicitly configured project checks. It is a suite inventory, not
|
||||
an enumeration of every test function or recursive dependency. Unselected suites
|
||||
remain visible; every row has a disposition and reason:
|
||||
|
||||
| Disposition | Meaning |
|
||||
| --- | --- |
|
||||
| `planned` | A selected stage directly invokes the suite. |
|
||||
| `covered_elsewhere` | An exact equivalent invocation or an explicitly selected shared batch owns it; the covering stage is named. |
|
||||
| `excluded` | The suite is outside the actual selected plan. |
|
||||
| `unsupported` | Scoped discovery cannot safely interpret or locate the command; no command is guessed or executed. |
|
||||
|
||||
`full` means the existing canonical `check-focused.sh` gate. It does **not**
|
||||
mean every package script or every component suite. Coverage reads the current
|
||||
script's explicit npm/Node commands inside its seven registered marked phase
|
||||
bodies, without executing shell code or inferring arbitrary functions, nested
|
||||
scripts, npm hooks, branches or here-document contents. Each covered suite names
|
||||
the `focused.<phase>` that owns it. The
|
||||
current gate names four of the sixteen Core component suites: layout primitives,
|
||||
page layout, DataGrid actions/sizing, and Mail components. The other twelve stay
|
||||
explicitly excluded from that gate's component batch. `ui` runs the shared
|
||||
sixteen-suite batch once; `quick` does not compile component tests.
|
||||
|
||||
Only Core's exact known component aliases receive that shared-batch treatment.
|
||||
A different repository using the same filename, an unknown alias, or a compound
|
||||
command containing the runner is not silently credited or launched. Ordinary
|
||||
scoped source discovery accepts bounded direct `node`/`node --test` commands
|
||||
targeting regular package-owned `.mjs` files in `scripts` or `tests`; shell
|
||||
chains, extra flags, missing targets and symlinked targets are not rewritten.
|
||||
An explicit invocation already present in the canonical full gate remains part
|
||||
of that gate, even when the narrower discovery profile does not support it.
|
||||
|
||||
Coverage rows retain a command hash and, for supported commands, redacted argv.
|
||||
Unsupported shell bodies are not copied into the inventory. This is display
|
||||
hygiene, not permission to put secrets into project commands. Coverage is
|
||||
attached to check plans/receipts; it never turns an excluded suite into a pass.
|
||||
|
||||
## Portable-project validation
|
||||
|
||||
The runtime validates custom project metadata against the published
|
||||
[`project.schema.json`](../../tools/devkit/project.schema.json) using a small
|
||||
dependency-free validator. All checks and profiles are validated before
|
||||
selection, including checks the chosen profile does not execute. Unknown nested
|
||||
fields such as `resource` or `timout_seconds`, malformed types, oversized values,
|
||||
duplicate references, missing dependencies, unknown profiles and dependency
|
||||
cycles fail with controlled errors. Repository aliases and resolved paths must
|
||||
be unambiguous and confined to the workspace. Check `cwd` defaults to `.` when
|
||||
omitted; configured paths cannot escape the workspace.
|
||||
|
||||
Package JSON is bounded to 1 MiB and 512 scripts; duplicate JSON keys, excessive
|
||||
nesting and malformed script objects fail before planning. Command strings are
|
||||
bounded to 8,192 characters. This validates configuration, not the safety of its
|
||||
code: run only project manifests and test scripts you trust.
|
||||
|
||||
Checks may declare repository-scoped `inputs`, true data `deps`, order-only
|
||||
`after`, and `reuse: "never"` for setup/output-producing work. Unknown repositories,
|
||||
empty/duplicate scopes, overlapping edge types and cycles fail before execution.
|
||||
Missing inputs deliberately fall back to all registered repositories. See
|
||||
[checkpoint and input contracts](DEVKIT.md#canonical-phases-and-narrower-inputs).
|
||||
Executable
+171
@@ -0,0 +1,171 @@
|
||||
# Devkit review bundles and issue evidence
|
||||
|
||||
Gitea remains the canonical backlog and review state log. These commands assemble
|
||||
guidance and append evidence; they do not create a second progress tracker, close
|
||||
issues, rewrite issue bodies, tick checkboxes, or certify a module review.
|
||||
|
||||
## Prepare a module review
|
||||
|
||||
```sh
|
||||
./devkit review campaign
|
||||
./devkit review campaigns --json --output /tmp/campaign-review.json
|
||||
./devkit review campaign --evidence RUN_ID
|
||||
```
|
||||
|
||||
Repository names, repository aliases and scope IDs from the
|
||||
[issue discovery inventory](../project/ui-review-issue-inventory.json) are accepted.
|
||||
`review bundle campaign` is an equivalent spelling. A bundle contains the current
|
||||
source inventory and fingerprint, canonical module/central issue links, the
|
||||
Core principle revision and content hash, applicable **planned** check stages,
|
||||
and a manual checklist. No checks or application servers are started. The
|
||||
optional output file is a local snapshot, not an authoritative state record.
|
||||
|
||||
The filename inventory is a starting point, not proof that all runtime surfaces
|
||||
were found. It does not import application manifests or optional modules. Review
|
||||
routes, panes, dialogs, settings, widgets, public forms, contributed/headless
|
||||
interfaces, missing-module behavior, permissions, English/German and narrow/wide
|
||||
layouts manually. Missing source, no UI files or no planned checks is never an
|
||||
automatic pass or N/A. Record exceptions and remaining work in the module issue.
|
||||
|
||||
Apply [UI-01 and UI-02, and the other Core principles](../../../govoplan-core/docs/UI_DESIGN_PRINCIPLES.md):
|
||||
books belong beside meaningful visible text; normal screens display compact
|
||||
data, with intentional scoped dialog editing. A bulk editing mode needs a
|
||||
documented reason. Record new principles and their back-propagation to previously
|
||||
reviewed modules through the [central review program](../project/UI_REVIEW_PROGRAM.md).
|
||||
|
||||
An explicit portable `--project project.json` does not inherit GovOPlaN issue
|
||||
links or principles. Its optional configuration is:
|
||||
|
||||
```json
|
||||
{
|
||||
"review": {
|
||||
"issue_inventory": "meta/docs/review-issues.json",
|
||||
"principles": "core/docs/UI_DESIGN_PRINCIPLES.md"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Paths are relative to `--workspace-root` and cannot escape it or traverse
|
||||
symlinks. The inventory uses schema version 1, `issues[]` with `scope_id`,
|
||||
`repository`, `number`, `url`, and optional `name`/`kind`; an optional `epic`
|
||||
contains `repository`, `number`, `url`. Snapshot issue-state fields are not
|
||||
reported as current state. Custom checks come from the project's selected
|
||||
profile (`--profile ui` by default).
|
||||
|
||||
## Preview and append evidence
|
||||
|
||||
```sh
|
||||
./devkit issues note --root govoplan-campaign --issue 103 \
|
||||
--evidence RUN_ID --summary 'Targeted checks passed.' \
|
||||
--next 'Manual module review and remaining fixes are still required.' --json
|
||||
|
||||
./devkit issues note --root govoplan-campaign --issue 103 \
|
||||
--evidence RUN_ID --summary 'Targeted checks passed.' \
|
||||
--next 'Manual module review and remaining fixes are still required.' \
|
||||
--env-file /private/gitea.env --apply
|
||||
```
|
||||
|
||||
`--root` is resolved relative to the selected **workspace root**, not the shell's
|
||||
current directory. In the normal siblings workspace, use `--root govoplan-campaign`
|
||||
or an absolute checkout path; do not use a path escaping the workspace.
|
||||
|
||||
The default is a completely offline dry run; it does not load credentials or
|
||||
contact Gitea. `--apply` explicitly authorizes comment creation. Tokens are read
|
||||
from `GITEA_TOKEN` or the explicit `--env-file` (environment wins); there is no
|
||||
implicit `.env` search. The credential file is parsed, never sourced, and is not
|
||||
printed or uploaded. Git's selected remote (default `origin`) determines the
|
||||
exact target: ambient `GITEA_OWNER`, `GITEA_REPO` and `GITEA_URL` cannot retarget
|
||||
the request. Credential-bearing HTTP Git remote URLs are rejected.
|
||||
|
||||
Use repeatable `--summary`/`--next`, `--body-file` for Markdown, or `--note-file`
|
||||
with structured JSON `{ "summary": ["..."], "next": ["..."], "body": "..." }`.
|
||||
Do not put secrets in notes, paths, check arguments or logs. Display redaction is
|
||||
best-effort protection, not permission to include confidential data.
|
||||
|
||||
For a bounded multi-issue operation, replace `--root`/`--issue` with
|
||||
`--target-plan targets.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"targets": [
|
||||
{
|
||||
"root": "govoplan-campaign",
|
||||
"issue": 103,
|
||||
"url": "https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/103"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Targets must be unique, inside the workspace and bound exactly to their Git
|
||||
remote/issue URLs, all on one exact Gitea base URL. Optional `issue_id` binds
|
||||
Gitea's immutable numeric ID as well. Every target, complete comment history,
|
||||
marker collision and local journal binding is checked before the first POST.
|
||||
Comments are then posted serially and read back against the exact issue. A
|
||||
closed issue stays closed; existing bodies, comments and checklists are untouched.
|
||||
|
||||
Each note has a stable hidden marker binding its issue, `--key` purpose
|
||||
(`verification` by default), and evidence run ID. Identical already-published
|
||||
notes are read back instead of reposted; changed content with the same marker
|
||||
is a collision, not an update. Use a distinct intentional `--key` for a separate
|
||||
follow-up. Without a receipt, the note's structured content determines identity.
|
||||
|
||||
A lost POST response is **not** retried automatically. The publisher scans all
|
||||
comment pages and reads back any matching comment. If it cannot confirm the
|
||||
result, it records `uncertain`, stops later posts, and exits nonzero. The next
|
||||
invocation reconciles again without replaying the POST. Only after inspecting
|
||||
the issue and local result, use `--apply --retry-uncertain` to permit one new
|
||||
attempt when the marker is still absent. Local journals and OS-released locks
|
||||
live in the workspace-scoped devkit state directory; do not delete them to bypass
|
||||
uncertainty. These are retry receipts, not issue state. Independent machines or
|
||||
different state directories cannot share a lock: coordinate publishers, because
|
||||
Gitea comment creation does not provide an atomic idempotency key.
|
||||
|
||||
## What a receipt does and does not prove
|
||||
|
||||
`--evidence RUN_ID` reads the workspace-scoped runner receipt and checks local
|
||||
integrity. `--evidence /path/to/receipt.json` accepts bounded validated external
|
||||
receipt metadata, explicitly labeled `external-unverified`. A self-consistent
|
||||
digest is not a signature or independent verification. Foreign-workspace,
|
||||
malformed and inconsistent successful receipts are rejected. Receipt-provided
|
||||
commands are never run; log files are not opened or uploaded, only referenced.
|
||||
Successful receipts must explicitly record a verified source snapshot; an
|
||||
external file's flag is still a reported claim, not an independent attestation.
|
||||
An aggregate containing a skipped stage cannot claim success. New versioned
|
||||
receipts additionally require independent verified checkpoints for passed phases.
|
||||
Failed/interrupted/stale aggregates remain labeled as such even when they contain
|
||||
reusable successful phases; reuse creates a new run and preserves the original.
|
||||
Stage coverage limitations are validated and retained in the note, including
|
||||
checks omitted by scoped profiles. Compact review output shows the first eight
|
||||
limitations and points to the complete list in its JSON bundle. A passing stage
|
||||
never implies that omitted compiler, browser, permutation or manual checks ran.
|
||||
|
||||
Documentation audit limitations are saved with their stage receipts, so later
|
||||
review bundles and issue notes retain the same scope as the original command.
|
||||
Managed inventory collectors use the explicit selected workspace throughout;
|
||||
an incomplete checkout never borrows sources or missing module imports from a
|
||||
fuller default workspace. Missing required Core/parser dependencies fail the
|
||||
audit. Legacy direct inventory commands without `--workspace-root` retain their
|
||||
discovery behavior; use the explicit option for reproducible evidence.
|
||||
|
||||
The note distinguishes `matches-current`, `historical-source-differs`,
|
||||
`different-project-not-compared` and unavailable comparisons. Matching source
|
||||
is a comparison at bundle/note preparation time, not a live test run or proof
|
||||
that a person completed review. If the source or environment has changed, run
|
||||
the appropriate checks again and keep historical evidence labeled honestly.
|
||||
|
||||
Versioned repository-input receipts compare the recorded repository scope, not a
|
||||
legacy whole-workspace hash. Partial scopes remain explicitly labeled in evidence
|
||||
notes. This comparison reads source only; receipt commands are never executed.
|
||||
Legacy receipts retain their original comparison format. A matching source scope
|
||||
does not prove that ignored build outputs, the environment or live services match.
|
||||
|
||||
Fixtures cover dry-run isolation, exact target bindings, capped/repeated
|
||||
pagination, collisions, append-only idempotency, uncertain POST reconciliation,
|
||||
secret-safe errors, receipt validation, portable projects and optional-module
|
||||
review boundaries:
|
||||
|
||||
```sh
|
||||
python3 -m pytest -q tests/test_devkit_issues.py tests/test_devkit_review.py
|
||||
```
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
# Selected-path Git maintenance
|
||||
|
||||
The optional `git` namespace of `tools/devkit/devkit.py` is a deliberately
|
||||
narrow maintenance workflow. It is not a replacement for normal Git, the
|
||||
release executor, required human review, or Gitea's canonical issue state.
|
||||
|
||||
From the Meta repository, preview and then save a plan for explicit files:
|
||||
|
||||
```sh
|
||||
python3 tools/devkit/devkit.py git plan --repo core --path webui/src/example.ts --message "Fix the reviewed example"
|
||||
python3 tools/devkit/devkit.py git plan --repo core --path webui/src/example.ts --message "Fix the reviewed example" --apply
|
||||
```
|
||||
|
||||
Use the saved `git-…` plan ID for the remaining commands. Each mutation needs
|
||||
its own `--apply`; an unqualified command only previews the operation:
|
||||
|
||||
```sh
|
||||
python3 tools/devkit/devkit.py git commit git-PLAN_ID
|
||||
python3 tools/devkit/devkit.py git commit git-PLAN_ID --apply
|
||||
python3 tools/devkit/devkit.py git push git-PLAN_ID
|
||||
python3 tools/devkit/devkit.py git push git-PLAN_ID --apply
|
||||
python3 tools/devkit/devkit.py git status git-PLAN_ID
|
||||
```
|
||||
|
||||
The immutable local plan binds the registered repository, HEAD, branch, origin
|
||||
fetch/push URL hashes, Git configuration, complete index identity, explicit
|
||||
selected file paths, working-tree hashes, expected Git blobs, and commit message.
|
||||
Raw remote URLs and file contents are not stored. A changed input blocks the
|
||||
operation; inspect it and create another plan instead of weakening the check.
|
||||
Receipts are private local records with integrity checks, not signed approval
|
||||
or a security attestation.
|
||||
|
||||
Commit captures each selected file once, verifies the captured bytes and Git
|
||||
blob against the plan, and builds an isolated index/tree from the frozen HEAD
|
||||
plus only those blobs. Git's normal identity rules create the commit object;
|
||||
an atomic branch compare-and-swap publishes it only if the planned parent is
|
||||
still current. Active hooks/signing remain refused rather than bypassed.
|
||||
An editor racing with this operation cannot substitute newer working-tree
|
||||
bytes: those edits remain uncommitted, and the helper never overwrites files.
|
||||
|
||||
The real index is protected by Git's standard index lock. Only selected entries
|
||||
are updated from a prepared private copy; unrelated staging and index flags are
|
||||
preserved. If the index changes independently, its new data is not overwritten
|
||||
and the receipt requires reconciliation. The resulting parent, message,
|
||||
changed paths, blobs/modes and unrelated index entries are verified.
|
||||
Git refs and index files are separately atomic, not one filesystem transaction.
|
||||
Independent writers that ignore the standard index lock can still change refs
|
||||
during publication. The helper rechecks the exact recorded candidate before
|
||||
index recovery and before recording success; it refuses an uncertain result
|
||||
instead of adopting a newer HEAD or claiming that competing work was its own.
|
||||
Directories, noncanonical paths, implicit globs, staging-all, amend,
|
||||
force-push and extra-tag publication are not supported. A selected file with
|
||||
different staged and working-tree changes is refused; decide explicitly which
|
||||
version to commit using normal Git first.
|
||||
|
||||
Push requires the recorded commit to remain current, an unchanged origin and
|
||||
one standard-transport push destination. It requests a normal, non-forced push
|
||||
of that commit to the recorded branch and checks the remote branch afterward.
|
||||
The push preview does not contact a remote. A normal push may run the remote's
|
||||
usual CI or server-side hooks; this is an external effect of the separately
|
||||
authorized push, not of planning or committing.
|
||||
|
||||
Local active hooks (including index-change hooks), filter/encoding attributes,
|
||||
signing, external fsmonitor/SSH configuration, Git environment overrides,
|
||||
in-progress merge/rebase/cherry-pick operations, submodules, symlinks and
|
||||
assume-unchanged/skip-worktree entries, split indexes and replacement/graft
|
||||
history are deliberately
|
||||
unsupported. The helper
|
||||
refuses these cases instead of disabling hooks, signatures or filters. An
|
||||
unused globally installed filter definition alone does not block maintenance;
|
||||
active attributes are checked across tracked files as well as selected files.
|
||||
Custom remote receive-pack/upload-pack/helper commands, recursive submodule
|
||||
pushes and partial-clone lazy fetch are also refused. Of inherited `GIT_*`
|
||||
variables, only `GIT_OPTIONAL_LOCKS`, `GIT_TERMINAL_PROMPT` and `GIT_PAGER` are
|
||||
allowed (every command explicitly passes `--no-pager`, so the pager is inert);
|
||||
namespace, identity, alternate-index/object-directory and unknown overrides
|
||||
are not silently removed. Git subprocesses have bounded input/output and
|
||||
deadlines; cancellation terminates their owned process group, including
|
||||
transport helpers. Network failures can still leave a remote effect uncertain,
|
||||
which is why receipts require explicit reconciliation rather than blind retry.
|
||||
|
||||
An interruption or uncertain failure is recorded before any retry. Use
|
||||
`git reconcile git-PLAN_ID` to preview, then add `--apply` to verify an
|
||||
already-existing result. Reconciliation never makes a commit or push. It may
|
||||
finish the selected real-index update after an interrupted commit publication,
|
||||
but only when the original index fingerprint still matches; independent staged
|
||||
work is preserved and requires normal Git resolution. For an
|
||||
uncertain push it may read the frozen remote, but only with `--apply`. If no
|
||||
planned commit is current, the helper reports that state and requires a fresh
|
||||
plan after inspection. A forcibly killed process may leave its private scratch
|
||||
index or an owned Git index lock; the helper never guesses that an existing
|
||||
lock is safe to delete. Blob/commit preparation can leave unreferenced Git
|
||||
objects for ordinary Git garbage collection. It never automatically resets,
|
||||
restores, deletes, or rolls back user work.
|
||||
|
||||
The maintenance tests use disposable local repositories and local bare remotes.
|
||||
They never commit or push the user's workspace repositories.
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
# Headless durable releases
|
||||
|
||||
The devkit `release` commands use the **same release application, request
|
||||
validation, run store and bounded executors** as the
|
||||
[release console](RELEASE_CONSOLE.md). They invoke its ASGI application inside
|
||||
the current process, with an ephemeral internal token. No listening socket,
|
||||
background server, browser session or externally supplied console URL is used.
|
||||
The development environment's FastAPI/HTTPX dependencies load only when a
|
||||
release command runs; ordinary devkit help does not import them.
|
||||
|
||||
This is a GovOPlaN-specific provider. Repository and origin authority still comes
|
||||
from the release service's registered catalog. `--project` is explicitly rejected
|
||||
for release commands; a portable project configuration must not silently override
|
||||
that authority. A different workspace path does not authorize arbitrary release
|
||||
repositories, remotes, commands, signing policies or source bindings.
|
||||
|
||||
The default compact output includes the overall status, selected repository
|
||||
states and versions, source-preflight readiness, bounded gate findings and the
|
||||
recommended next action. Run inspection also shows step-state counts and the
|
||||
first outstanding steps. These are projections of the service response, not new
|
||||
readiness checks. Use `--json` for the complete existing plan or receipt; compact
|
||||
output deliberately omits executor commands, arguments and source bindings.
|
||||
|
||||
## Commands and boundaries
|
||||
|
||||
| Command | Purpose | Effect boundary |
|
||||
| --- | --- | --- |
|
||||
| `release status` | Current release dashboard | Offline by default; explicit remote/catalog flags enable their checks |
|
||||
| `release plan` | Selective repository/version plan | Inspection only; does not create a run |
|
||||
| `release list` | Bounded run history, with `--limit` and `--cursor` | Existing workspace-scoped store |
|
||||
| `release show RUN_ID` | Exact verified run, steps, receipts and required confirmations | No release execution |
|
||||
| `release create` | Freeze a selected repository/version plan | Preview by default; `--apply` persists a run |
|
||||
| `release preview RUN_ID STEP_ID` | Frozen step and its current constraints | No step claim; catalog publication additionally uses the existing validated-candidate preview |
|
||||
| `release execute RUN_ID STEP_ID` | One available durable step | Preview by default; `--apply`, explicit request ID and the step's exact confirmation are required |
|
||||
| `release resume RUN_ID` | Mark persisted running attempts interrupted | Requires `--apply`; never assumes an effect succeeded |
|
||||
| `release retry RUN_ID STEP_ID` | Prepare an eligible failed/read-only-interrupted step | Requires `--apply`; does not execute it |
|
||||
| `release reconcile RUN_ID STEP_ID` | Record a proven uncertain-write outcome | Requires `--apply`, `--confirm RECONCILE` and an explicit outcome |
|
||||
|
||||
Create and transition commands require caller-chosen `--request-id` values.
|
||||
Retain the same ID for an uncertain replay of the **same** command; changing
|
||||
inputs under an existing ID fails closed. No command automatically retries a
|
||||
mutation. These commands perform one explicit transition, not an implicit
|
||||
"release everything" loop.
|
||||
|
||||
Read commands do not commit, tag, push, publish or apply database migrations.
|
||||
The existing private run store can initialize its lock directory or upgrade a
|
||||
legacy record while inspecting it; this does not advance release steps.
|
||||
`--include-migrations` requests the existing audits, never migration application.
|
||||
`--online`, `--remote-tags` and `--public-catalog` are explicit network-check
|
||||
choices. A generic step preview describes frozen intent; it is not a claim that
|
||||
live preflight, remote identity or release acceptance has passed.
|
||||
|
||||
## Example: review, freeze and execute one step
|
||||
|
||||
Run the devkit through the workspace `./devkit` entrypoint. These examples put
|
||||
global options before `release`:
|
||||
|
||||
```sh
|
||||
./devkit --workspace-root /path/to/workspace --format json release plan \
|
||||
--repo-version govoplan-files=0.1.9
|
||||
|
||||
./devkit --workspace-root /path/to/workspace release create \
|
||||
--repo-version govoplan-files=0.1.9 --request-id files-release-create-0001
|
||||
|
||||
./devkit --workspace-root /path/to/workspace release create \
|
||||
--repo-version govoplan-files=0.1.9 --request-id files-release-create-0001 --apply
|
||||
|
||||
./devkit --workspace-root /path/to/workspace release show RUN_ID
|
||||
./devkit --workspace-root /path/to/workspace release preview RUN_ID STEP_ID
|
||||
./devkit --workspace-root /path/to/workspace release execute RUN_ID STEP_ID \
|
||||
--request-id files-release-step-0001 --confirm REQUIRED_CONFIRMATION --apply
|
||||
```
|
||||
|
||||
`RUN_ID`, `STEP_ID` and `REQUIRED_CONFIRMATION` stand for the actual values
|
||||
returned by the frozen run. For read-only preflight/alignment/install-verification
|
||||
steps, omit `--confirm` when the service reports an empty confirmation. Metadata,
|
||||
commit, release-lock, source-tag, source-push, candidate-generation and catalog
|
||||
publication steps keep the existing `UPDATE`, `COMMIT`, `LOCK`, `TAG`, `PUBLISH`,
|
||||
`GENERATE` and `PUSH` confirmations respectively. Prerequisite ordering is enforced
|
||||
by the same store; a later step cannot be forced through this adapter.
|
||||
|
||||
Version selection supports repeated `--repo-version REPO=VERSION`, or repeated
|
||||
`--repo REPO` with `--target-version VERSION`. Every created run needs explicit
|
||||
versions for all selected repositories. Conflicting version assignments are
|
||||
rejected. Planning is selective; repositories are never silently selected merely
|
||||
because their worktrees are dirty.
|
||||
|
||||
## Candidate publication and recovery
|
||||
|
||||
Use the frozen run's `catalog:selective-generator` and
|
||||
`catalog:validate-sign-publish` steps, not the disabled legacy mutation endpoints.
|
||||
Generation accepts repeated `--signing-key KEY_ID=PRIVATE_KEY_FILE`; these are
|
||||
operator-owned key-file references, never inline key material. Signing arguments
|
||||
are not emitted in devkit JSON or persisted by the adapter. Publication consumes
|
||||
the candidate receipt created by that run, not an arbitrary candidate directory.
|
||||
The existing source/runtime trust, immutable tag/remote identity, private
|
||||
candidate checks and exact commit-delta checks remain authoritative.
|
||||
|
||||
After an interrupted write, inspect the run and the actual local/remote effect.
|
||||
Use `resume --apply` if an attempt was left running. Then use `reconcile` with
|
||||
`effect_absent`, `effect_succeeded` or `unresolved`, `--confirm RECONCILE`, a new
|
||||
reconciliation request ID and `--apply`. Successful reconciliation still performs
|
||||
the service's independent receipt checks. An unresolved effect must not be
|
||||
retried or papered over by generating a new request ID. A failed/read-only
|
||||
attempt may use `retry --apply` and a new explicit execute attempt only when
|
||||
the existing lifecycle makes that safe.
|
||||
|
||||
## State and limitations
|
||||
|
||||
Every successful response exposes `state_location` and `candidate_location`.
|
||||
Without `--state-dir`, the adapter uses the console's existing default private,
|
||||
workspace-fingerprinted state directory. With `--state-dir PATH`, it uses
|
||||
`PATH/release-console/workspace-<fingerprint>/release-runs`. Workspaces cannot
|
||||
read or resume one another's runs. Existing ownership, symlink, permission,
|
||||
retention and record-integrity checks are unchanged.
|
||||
|
||||
Release preparation can commit only the recognized, receipt-bound metadata
|
||||
changes its executors produced. This command does **not** stage arbitrary source
|
||||
changes, use `git add -A`, force-push, retarget tags or enable the disabled generic
|
||||
prepare/sync/push endpoints. Normal selected-path maintenance requires its own
|
||||
explicit reviewed-change workflow; it is not silently folded into release.
|
||||
@@ -354,8 +354,17 @@ such as `0.2.0` or `0.2.0-alpha1`, but requires the first three version numbers
|
||||
to move forward.
|
||||
|
||||
Plain repository pushes are separate from catalog publication. `Preview Push`
|
||||
shows the selected repository push commands. `Push Selected` requires `PUSH` in
|
||||
the repository push confirmation field.
|
||||
shows the selected repository push commands, but generic push, sync and prepare
|
||||
mutation endpoints are disabled: they require a separate durable, receipt-bound
|
||||
maintenance workflow and cannot be enabled by typing a confirmation. Source
|
||||
release branch/tag publication uses the durable release-run steps described
|
||||
above; do not route ordinary dirty worktrees through the legacy all-repository
|
||||
stage/commit/tag helper.
|
||||
|
||||
The [headless devkit release commands](DEVKIT_RELEASE.md) invoke this same
|
||||
application in-process without starting a server. They expose selective planning,
|
||||
bounded status/history, create/show/preview/execute and explicit recovery while
|
||||
retaining the same request IDs, confirmations, source bindings and receipts.
|
||||
|
||||
The source release panel retains `Preview Tag + Publish` as a non-mutating
|
||||
inspection. Its legacy `Create Tags` and `Publish Tags` controls stay visible
|
||||
|
||||
Reference in New Issue
Block a user