AISDLC Companion
Code name forge. Maintained by the AISDLC team.
A VS Code extension that gives humans a dedicated interface to AISDLC forges. A forge normally
runs in GitLab CI. With the companion you start a forge for one ticket on your own machine,
follow what the agents produced, and work on the repositories of the ticket.
The extension is client- and ticket-agnostic. A forge is a fork of forge-blueprint named
<client>-forge on GitLab and is shown as forge-<client>.
Pages and views
Welcome (editor tab)
Command AISDLC: Welcome / Overview, or the home icon in the Forges view title. It opens by
itself on the first window after the extension is installed, and after an update it offers a
What's New button instead of opening on its own.
It is the entry point of the extension: a live status summary (services connected, Docker, forges
on this machine), one card per page with a button that opens it, the three steps of starting a
forge, and links to your GitLab and Jira hosts once they are configured.
Setup (editor tab)
Command AISDLC: Open Setup, or the gear in the Forges view. One card per service, each with a
Connect button that opens a dialog. The dialog tests the credentials before saving them.
| Service |
Used for |
| GitLab |
Lists your forges and their tickets, fetches the default branch of a forge. Token scopes: api, read_repository, read_registry |
| Jira |
Ticket summaries; the agents read and update the ticket |
| AI model provider |
FACTORY_API_KEY or POLARIS_API_KEY, used by the agents |
| Container registry |
docker login to the registry of the pipeline image the base environment is built on |
Tokens are stored encrypted in VS Code secret storage. A token saved here wins over the same
variable in the OS environment; Disconnect falls back to the OS value. Tokens never go to
settings, logs or command lines.
Below the cards, the page checks Docker, Git, the Dev Containers extension and local forge folders.
On Windows, when docker is only a .cmd wrapper around WSL, the extension detects it and calls
wsl --exec docker directly.
Start a Forge (editor tab)
Command AISDLC: Start a Forge, or + in the Forges view. The page titles itself Create
Forge, under a Home > Create Forge breadcrumb, the way the landing page and the forge dashboard
do.
- Forge: a dropdown of your GitLab projects named
<client>-forge, plus forge folders cloned on
this machine. A GitLab forge is listed without its forge- marker (acme), a local folder under
the name it was added with.
- Ticket: a table with one row per ticket and a column per field (Ticket, Summary, Status,
Branch), not a dropdown. Tickets come from
agents/tickets/index.yaml of the forge, plus every
agent/<ticket> branch, ordered alphabetically by id (ABC-9 before ABC-10). Summaries and
statuses come from Jira when it is connected. One row is selected at a time, and the selected row
is the ticket the forge is started for.
- Ticket detail: once a ticket is picked, its title, description, labels and current assignee
appear below the list, with a link to the issue in Jira. The title, description, labels and
assignee come from Jira; the description falls back to the
index.yaml entry.
- Flow and Role: the flow defaults to the one named by the ticket's
agent:<flow>_xxx
label (the first such label wins), then to the one in the ticket's forge status.
The branch is implied by the ticket: the container always works on its agent/<ticket> branch,
which it creates when it does not exist yet. There is nothing to choose.
Start Forge then runs four steps, shown live on the page:
- Fetch forge: shallow clone of the repository's default branch into a temporary folder. The
GitLab token is passed through
GIT_CONFIG_* environment variables only and is never written to
the clone. Skipped when the forge's base environment is already built.
- Build base environment: builds one image per forge locally, from the
docker/Dockerfile
shipped with this extension. The clone is the build context, so the image carries the whole
framework; the build recipe itself comes from the extension, so a forge branch cannot change how a
forge is built. Skipped when it is already built.
- Start forge: starts a fresh container on that base image with your connected tokens. The
entrypoint fetches and checks out the ticket's
agent/<ticket> branch, then the framework clones
the ticket's repositories itself. CI_JOB_TOKEN is set to a fixed placeholder: the framework
refuses an empty value, but a local run has no job token and never uses it as a credential.
- Prepare the workspace: waits for the container to say it is done preparing.
docker run --detach returns as soon as the container exists, while the checkout and the clone above are
still running, so the launch follows the container's log until the entrypoint logs setup complete; container remains attachable. The step counts the seconds it has waited; the page
closes and the landing page opens only then, so it paints a flow that can be read instead of
spinning for the seconds the clone takes. A preparation that fails, stops, or takes over ten
minutes keeps the page, its log and the container, and says which of the three it was.
The image is per forge and flow-agnostic: <forge>-base, everything lowercased —
forge-of-the-forge-base. It is built the first time a forge is started on this machine and kept
until you remove it, so a second ticket of the same forge starts straight away. The container stays
per ticket/flow/role: <forge>-<flow>-<JIRA_ID>, with the stage appended when a role is chosen —
forge-of-the-forge-dev-aisdlct-177, or forge-of-the-forge-dev-developer-aisdlct-177 for the
developer role of the dev flow.
Because the base is a snapshot, it does not pick up new commits on the default branch on its own.
Remove Base Environment on the forge row drops it; the next launch rebuilds it. It is refused
while a container of that forge still runs on it. When a container for the ticket already exists, the
page offers to use it or replace it.
The per-ticket images an earlier version built (<forge>-<flow>-<ticket>) are no longer addressed by
anything in the UI: nothing runs on them once their containers are gone, so docker image prune
reclaims them.
Once the forge has started, this tab closes itself and the landing page of the new forge opens in
its place: the stages of its flow, and the reports the agents produce as they run. A launch that
fails, or that is refused because a forge already exists, keeps the page open with the log instead.
Landing page (editor tab)
Command AISDLC: Open Landing Page, or the graph icon on the row of a running forge — and what
opens when a forge starts. It shows where the ticket stands in the flow of its forge:
INTSP-3033 · dev status: dev_to_implement
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ Dev Classifier │ → │ Dev Developer │ → │ Dev Reviewer │
│ classifier │ │ developer │ │ reviewer │
│ done │ │ current │ │ did not run │
│ 00-ticket.md │ │ 02-plan.md │ │ 05-review.md │
│ 01-analysis.md │ │ 03-impl…md │ │ 04-test.md │
│ 02-plan.md │ │ 04-test.md │ │ │
└────────────────┘ └────────────────┘ └────────────────┘
This is the global flow: every stage of the flow, side by side, left to right — the overview of
where the ticket stands. It is not the chronological record; that is the Run history below, which
lists the stages as they actually ran, one row each, in the order they happened.
dev_to_implement is what the classifier leaves behind, and what triggers the developer: the
classifier is past, the developer is current, the reviewer has not run. The reports the last two
promise are struck through until they are written.
The stages come from the container itself, not from a list kept in the extension. Each role of the
flow carries a pipeline.yml (automated-flow/<flow>/<role>/pipeline.yml) whose variables describe
it: STATUS_TRIGGER is the ticket status that starts the role, STATUS_SUCCESS the one it leaves
behind, STATUS_WORKING the one it sets while it runs, STATUS_FAILURE the one it leaves on
failure, and FORGE_ARTIFACT_EXPECTED the markdowns it is expected to write.
A forge that keeps no such files declares its stages in its .gitlab-ci.yml instead. The extension
then reads the root CI file and every file it include:s locally, concatenates them, resolves each
job's extends: (a job with none is a root; a job that extends one is merged into it, its own
variables winning and its before_script/script/after_script appended after the parent's), and
takes the jobs that declare both FORGE_FLOW and FORGE_STAGE for the flow being read — those
jobs carry the same STATUS_* and FORGE_ARTIFACT_EXPECTED variables the per-role files do.
The order of the chart is read from those statuses: when the STATUS_SUCCESS of a stage is the
STATUS_TRIGGER of another, the two are chained, and the chart follows the chain from the stage
nothing triggers. A stage the chain does not reach is still shown, after the others, so a role is
never silently dropped.
The colors come from the ticket's status in the forge's agents/tickets/index.yaml:
| Color |
Meaning |
| green |
the ticket is past the stage: it ran |
| yellow |
the stage is the current one — its trigger, or its working status |
| red |
the ticket holds a STATUS_FAILURE; the stage shown is the one whose run failed, which is the one the history below records as failed (every role of a flow often shares one failure status, so the status alone cannot say) — the last stage that produced artifacts when no history is readable |
| gray |
the ticket is before the stage: it did not run |
| gray (all) |
the status matches nothing in this flow, so the chart shows no progress rather than guessing |
Under each stage is the list of the markdowns FORGE_ARTIFACT_EXPECTED promises, matched against the
reports of the ticket in /workspace/agents/tickets/<ticket>/<flow>/ (the ticket folder when the
reports are there instead). An entry that is not there yet is shown struck through. Clicking one
opens it in VS Code's own markdown preview, in the column beside the landing page — the extension
renders no markdown of its own. Refresh re-reads the container, so the chart follows a running
forge.
Below the chart, a Run history section — folded by default — holds the literal record of
agents/tickets/<ticket>/.jira-pipeline/rows.tsv: one row per pipeline run, in the order it
happened, running down the page and joined by a downward link, with the role, start time, status,
duration, summary and a link to the pipeline in GitLab. Where the chart shows a stage once, the
history shows it as many times as it ran, so a retry, or a loop back to a stage the ticket had
already passed, is visible — a run that returns to a stage the ticket had passed is drawn as a step
back rather than forward. Each stage tile of the chart also carries a quiet count
(2 runs · last 09:34 · 1 failed) once it has run.
The history lists every run of the ticket, including the ones recorded by a flow this container does
not carry — a qa container whose ticket went through dev stages — but the chart itself stays on this
container's own flow.
Each run card also lists what that step produced, folded into a quiet count at the foot of the
card (3 artifacts ▸). A stage commits its artifacts as <FORGE_JOB_NAME>: <TICKET> and persists its
state separately as <FORGE_JOB_NAME>: finalize <TICKET>; only the first is read, so the list holds
the work of the stage rather than the framework's bookkeeping. In the orchestrator those are the
agents' reports — the numbered NN-*.md files; the notes under agents/memory/, index.yaml and
the per-job JSON say nothing about what the stage produced, so they are left out. In a repository the
ticket cloned under repositories/, the whole commit is the artifact, because that commit is the
code change. A run is matched to its commit by the job name — Dev Developer and Developer name the same
role — and by time, so a retry gets its own commit, and a run that failed before committing anything
lists none rather than borrowing its neighbour's.
The list keeps the shape of the repository: the files of each repository sit in a foldable tree
whose folders unfold like the Explorer's, rather than in one flat run of full paths. A folder whose
only child is another folder is joined with it into one node (src/main/java/com/comarch/), so a
Java package shows as the single path it is instead of a chain of folders that each hold nothing.
Clicking an artifact opens it in VS Code's own diff editor, against the revision of the file
before that commit: an added file against an empty document, a modified one against its previous
version. The diff opens beside the landing page, which keeps the left half of the window, and
each further artifact is added as a tab of that same right column rather than replacing the one
before it — so the page stays put while each artifact's diff waits its turn. Both sides are read out
of git through the forge:// provider, so the diff shows what that one run changed even after later
runs have moved the file on, and neither side can be saved over the live file.
A forge prepares its workspace with git clone --depth 1, and the framework clones the ticket's
repositories as blobless partial clones, so neither carries more than its tip: the commits a stage
produced are not there to be listed, and without help every card of every container would claim its
stage wrote nothing. The extension therefore widens the history first, in place and in one call
(git fetch --unshallow per clone), and only then reads the log — which is why the reports of the
orchestrator (00-ticket.md, 01-analysis.md, …) now appear where a shallow workspace used to list
none. The widen writes into .git, which the image owns as root, so that one call runs as root; the
log itself stays an ordinary read as the container user. A clone the widen cannot reach — an
unreachable remote — stays shallow, and the history says so once at the top, instead of letting every
card look as though its stage produced nothing.
Forge dashboard (editor tab)
Command AISDLC: Open Forge Dashboard, or the dashboard icon on a forge row — the client row of
the Forges view, above its tickets. Where the landing page shows the flow of one ticket, the
dashboard shows the whole forge: every ticket container it has on this machine, with the state of
each, the flow and role it runs, its branch, and how far it got in its flow (the same stage states as
the landing page, as a bar).
| Column |
What it shows |
| Ticket |
The Jira key, and the container name below it. |
| State |
The state as a colored dot: running, paused or ready. |
| Flow |
One segment per stage in execution order, colored from the ticket's status. |
| Branch |
The flow and role, the agent/<ticket> branch, and the start time of a running container. |
|
The actions of the row: a terminal and stop for a running forge, resume for a paused one. |
The ticket of the first column opens the landing page of a running forge, so the dashboard and
the side bar lead to the same page from the row itself; a forge that is not up is started from the
side bar, which knows its own state.
The page follows Docker: a forge that starts or stops moves its row without a click. Refresh
re-reads the containers. A ticket that has no flow, or whose flow cannot be read, still shows its
state — the row explains what is missing instead of hiding the ticket.
Forges (side bar)
Only what exists on this machine:
forge-acme 2 tickets
ABC-12 dev
Agent output agents/tickets/ABC-12
Repositories
acme-backend agent/ABC-12
ABC-9 qa
forge-beta
BETA-3 dev
Forge states: running, paused (stopped, kept). The row's icon and its actions carry the
state — the description shows only the flow and role. Actions: start, open terminal, open in a new
window (Dev Containers), show activity, stop, resume, discard, start another forge from the same
client.
On a running forge the row carries, in this order:
- a graph (AISDLC: Open Landing Page) — the flow of the ticket, its current stage and the
reports each stage is expected to produce (see Landing page);
- a terminal (AISDLC: Open Forge Terminal);
- a red square (AISDLC: Stop Forge) — stops the container and keeps it, so the ticket can be
resumed;
- an X (AISDLC: Delete Forge and Container) — removes the container in one step, whether
it runs or is stopped. The forge's base environment is shared with its other tickets, so it is
always kept; nothing else is kept for a quick restart, but Discard states that explicitly.
A forge that is not running carries a green play (AISDLC: Start Forge) in the square's
place — the two are never shown together, because a container is either running or not. The play
resumes a paused container.
A client row (the forge itself) carries, besides its +, a dashboard (AISDLC: Open Forge
Dashboard) — the overview of every ticket container of that client (see
Forge dashboard) — and, once its base environment exists, a trash
(AISDLC: Remove Base Environment) that drops it. The forge's tooltip names the base ref it is
built from and its default branch; a forge created here is never silently rebuilt.
The play and square buttons are drawn as SVG icons of the extension (media/), one per theme, so
they keep their color — green and red — instead of following the icon color of the theme. Both are
outlined (no fill), 16×16, and cover the same area as the X.
Every container on the machine is simply listed; there is no "open here" marking, no per-window
status-bar entry, and no notion of a forge being attached to the current window. The commands that
take no row — Open in New Window, Open Forge Terminal Here — act on the only running forge, and
ask which one when several are.
While a stop, a delete or a start is in flight, the row is greyed out with a spinner and offers no
action, until Docker has settled — the play and Resume spin their row the same way Stop
does. Such a row also cannot be expanded.
A running forge expands into:
- Agent output: the flow folder of the session,
/workspace/agents/tickets/<ticket>/<flow>, or the
ticket folder /workspace/agents/tickets/<ticket>/ when the reports are there instead (a ticket
prepared before the framework ran one folder per flow, or one whose flow folder is empty).
Whichever it opens, it shows the reports of the agents only.
They are the numbered .md files (00-ticket.md, 03-implementation.md,
05-review_failed-jobs_<repo>.md); research_report.md, index.yaml and the JSON settings around
them stay hidden, and so does a folder that holds no report, dev/ and qa/ excepted when they
carry their flow's reports.
- Repositories: each clone under
/workspace/repositories/, with branch and access.
Files open through the forge://<container>/<path> file system, run as agent_user. What can be
edited, created, renamed or deleted is decided by that user's permissions in the container — the
same permissions a terminal has; the extension adds no rule of its own. Paths outside /workspace
are refused.
Forges started outside the extension (for example with the forge's docker compose setup) are
listed too, and can be opened in a Dev Containers window from their row.
Deep links
Every page can be opened from a link, so a website, a Jira ticket or a wiki can carry an
Open with AISDLC Companion button:
<a href="vscode://ComarchSA.aisdlc-companion/forge/acme/ABC-1">Open with AISDLC Companion</a>
The browser asks to open VS Code, the extension activates (onUri), the window is focused, and the
page opens.
| Link |
Page |
vscode://ComarchSA.aisdlc-companion/ or /home |
Welcome / Overview |
vscode://ComarchSA.aisdlc-companion/setup |
Setup; ?service=gitlab\|jira\|model\|registry focuses a card |
vscode://ComarchSA.aisdlc-companion/new?client=acme&ticket=ABC-1 |
Start a Forge, with optional presets |
vscode://ComarchSA.aisdlc-companion/forge or /forge/acme |
Forge dashboard (bare /forge asks which forge) |
vscode://ComarchSA.aisdlc-companion/forge/acme/ABC-1 |
Landing page of a ticket |
A client may be written acme, acme-forge or forge-acme; a ticket id is case-insensitive. A link
to a forge or ticket that is not on this machine warns instead of opening an empty page, and an
unknown link lists the supported forms.
The links carry no credentials and can open no destructive action: the reserved action parameter
is an allowlist that is empty today, and any action added later must confirm inside VS Code before it
does anything. Only a page is a link.
Settings
| Setting |
Default |
Purpose |
forge.docker.command |
["docker"] |
Docker command, e.g. ["wsl", "docker"] |
forge.localForges |
[] |
Local forge clones: name (<client>-forge) and path |
forge.localForgeSearchRoots |
[] |
Folders scanned one level deep for local forge clones (a folder is a forge when it carries automated-flow/common/framework/setup-environment.sh and agents/repo-knowledge/) |
forge.image.pipelineImage |
GitLab developer image |
FORGE_PIPELINE_IMAGE; empty keeps the extension Dockerfile default |
forge.runtime.modelProvider |
factory |
FORGE_MODEL_PROVIDER; also set from the Setup page |
forge.runtime.extraEnv |
[] |
Extra variable names forwarded to new forges |
forge.container.user |
agent_user |
User for file access and terminals |
forge.container.workspaceRoot |
/workspace |
Workspace root inside a forge |
forge.refreshIntervalSeconds |
15 |
Auto-refresh of the Forges view; 0 disables it |
forge.workspace.exclude |
[".git"] |
Names hidden in forge files |
GitLab and Jira URLs come from the Setup page, then GITLAB_BASE_URL / JIRA_BASE_URL, then the
Comarch defaults.
Limitations
- GitLab listing and fetching need network access to the GitLab host (VPN).
- Text search across
forge:// folders is not available (VS Code has no stable search API for
custom file systems). Use a forge terminal or a Dev Container window.
- With Docker inside WSL, the Dev Containers extension needs
dev.containers.executeInWSL enabled
to open a forge window.
- Jira authentication uses a bearer personal access token (Jira Data Center).
Development
npm install
npm run typecheck
npm test # unit tests
FORGE_LIVE_CONTAINER=<name> npm test # + live tests against a running forge
FORGE_LIVE_CONTAINER=<name> npm run smoke # extension smoke test in an isolated VS Code
npm run bundle # dist/extension.js
npm run package # forge-<version>.vsix
Source layout: src/core/ holds pure logic with unit tests (no vscode import); src/ holds the
VS Code layer; src/pages/ the editor-tab webviews (Welcome, Setup, Start a Forge, landing page,
forge dashboard); src/views/ the Forges tree; docker/ the build recipe of every forge image.
Build recipe (docker/)
docker/Dockerfile, docker/forge-local-entrypoint.sh and docker/Dockerfile.dockerignore are the
build recipe of a forge image. They are copied from the corresponding files of the forge
repositories and must stay in sync with them — the stage whitelist of src/core/flows.ts mirrors the
entrypoint's. docker/Dockerfile differs in one place: the entrypoint is taken from a second, named
build context (--build-context forgeassets=<extension>/docker) instead of the forge clone, which is
the main build context. The .dockerignore is named Dockerfile.dockerignore because Docker reads
the ignore file of the context, and the context is the forge clone, not this folder.
The context is a checkout of the forge's default branch, and .git must stay in it: the
entrypoint fetches and checks out the ticket's agent/<ticket> branch at container start, and flow
and stage arrive as runtime env. The image therefore carries no ticket, flow or stage — one base image
per forge, labelled com.aisdlc.forge.kind=base.
Page layout (final direction)
Every page is built with pageHtml() from src/pages/webviewKit.ts, and BASE_CSS in that file
owns the shared look:
- Background: the editor's own theme background (
--vscode-editor-background), never a hardcoded
color, so pages follow light and dark themes.
- Layout: the page fills the editor it is opened in — no width cap and no centered column. The
body padding (
20px 28px 40px) is the only inset.
A new page must not restate either rule. It must not set its own body { max-width } or
margin: 0 auto: the page uses all the space the editor gives it, and a page that wants to constrain
what it draws constrains its own content (a ch-capped paragraph, a fixed-width chart card), never
the page. All colors come from --vscode-* variables, so no page breaks under a theme switch.