Remediation
Project Sightline names vulnerable repositories. Three public forks then carried those findings through an agentic pipeline: BC Parks attendance and revenue admin, CSCP restitution, and LEAR.
C.S.B.C - Connected Services B.C
A.I Technical Solutions Research
A field account from the AI Technical Solutions team: how a vulnerability report becomes a reviewed change, how a living application was rebuilt from its own behaviour, and which parts of Alberta’s AI Garage and AI Factory are ready for another team to copy.
On this page: Why it matters Two patterns Remediation Rebuild Alberta What worked What failed Try it
9 October 2026
The runs already happened. The pull requests, gate ledgers, and slide decks sat in repositories this team could see and other teams could not use. This page is the public record. It does not wait on another ministry, a vendor, or a central platform team. Where a number is below, it was counted from a public repository or taken from a named source. Where a number does not exist, the page says so.
Project Sightline names vulnerable repositories. Three public forks then carried those findings through an agentic pipeline: BC Parks attendance and revenue admin, CSCP restitution, and LEAR.
digital-marketplace-next rebuilds the Digital Marketplace from recovered behaviour. It is an experiment. It has never been deployed and must not be used with real data.
The AI Garage fixes a system in place. The AI Factory builds a new one the government owns. We used both as the map for the runs above, and we kept their numbers labelled as theirs.
The Ministry of Transportation CIS Java remediation belongs to Rogene Lacanienta’s team. It is not in this paper. See that section.
The map
Alberta’s published framing, in The Four Approaches to AI Modernization, is the one we used. The Garage repairs a system where it stands. The Factory builds a new application against one specification, with specialist agents checking each other and a person accepting or rejecting the result. A scan is the input to that choice. It is not the delivery.
| Pattern | When it fits | What we actually ran |
|---|---|---|
| AI Garage | The application should keep living, and the defect is local enough to patch under tests. | Sightline-style rapid assessments turned into issues, then spec, plan, and implementation pull requests on three forks. |
| AI Factory | The application is tangled, vendor-locked, or cheaper to replace than to patch safely. | Digital Marketplace rebuilt in 21 slices from acceptance criteria recovered from the original system. |
| Project Sightline | You need a list of repositories with vulnerability and technical-debt findings. | Used as a selector and a backlog source. It does not name an owner, a business case, data sensitivity, or whether to fix, rebuild, or retire. |
That last row is the practical limit. A report can start a conversation with the team that owns the system. It cannot sign a checkpoint. The worst findings in this kind of scan sit on applications nobody has claimed. If nobody will accept a merge, the pipeline has nothing to attach to.
Garage, on our code
Each run started from a rapid assessment, filed the recommended findings as GitHub issues, and sent every finding through a specification, a plan, and an implementation pull request. Agents do not merge. A person, or in the parks pilot a simulated reviewer following the same checklist, accepts the change. After the backlog closed, a separate pass re-read the original finding against the code, because a closed issue is not the same thing as a fixed defect.
Repository: bcgov/bcparks-ar-admin-agentic-b. An earlier fork, bcgov/bcparks-ar-admin-agentic, was the first enrolment and was left as a partial pilot. The numbers below are from the second fork.
| Measure | Result |
|---|---|
| Recommended findings shipped | 45 of 45. Seven optional and three duplicate rows were left untouched on purpose. |
| Wall clock | First pull request 31 Aug 2026 21:36 UTC, last merge 1 Sep 2026 18:18 UTC. About 20.7 hours for the whole backlog, about 28 minutes of calendar time per finding. |
| Pull requests | 149 merged, 7 closed without merge. Open-to-merge time is typically under a minute, because the change was already built before the pull request was opened. That clock is not reviewer effort. |
| Checkpoint gate | 93.8% after 5 slices, 97% after 10. The later full-backlog artifact stopped reporting the rate; sampled pull requests stayed in the same range. |
| Spec trace | Implementation pull requests missing a spec reference: 0% across the pass. 79 criterion ids ended in the registry. |
| Independent re-check | 35 fixed, 8 partial, 2 not fixed, against the original assessment wording rather than the closed issue. |
The two misses and most of the partials have a cause, and it is useful. One implementation pull request
merged an evidence file and no code: the narrative said the logging levels had changed, and
keycloak.service.ts had not. Several other findings were signed at a narrower scope than the
assessment asked for, then verified against that narrower contract. The pipeline was faithful to the
contract and incomplete against the original defect. Reviewer effort on this fork was a simulated
checkpoint, so the 97% figure is a gate result, not a count of hours a person spent reading diffs.
Repository:
bcgov/pssg-cscp-restitution-agentic,
on branch development. Copilot’s coding agent implemented most slices. An operator had to
nudge Actions, and finished the tail of the backlog by hand when credits ran out.
| Measure | Result |
|---|---|
| Recommended findings shipped | 36 of 36. Three informational findings were never filed. |
| Wall clock | First pull request 3 Sep 2026 19:12 UTC, last merge 4 Sep 2026 20:23 UTC. About 25.2 hours, about 42 minutes of calendar time per finding. |
| Pull requests | 108 merged, 0 closed without merge. That is one specification, one plan, and one implementation pull request per finding. |
| Independent re-check | 35 fixed, 1 partial, 0 not fixed. The partial is VULN-004: the signed slice added maximum lengths and left email and phone format checks for a later slice. The implementation matched the signed scope. |
| Who did the work | Copilot for the early and middle slices. From finding 30 onward, Copilot credits were exhausted and the operator shipped the remaining slices locally, still through the same spec, plan, and review comments. |
Reviewer effort here was real and narrow. The operator’s repeated job was an empty commit so GitHub Actions
would leave the action_required state and actually run the checkpoint gate. A CodeQL
default-setup error failed the aggregate gate on slice after slice even when Trivy and the tests were
green. The team recorded that residual and kept moving, which kept the backlog moving and also trained
everyone to ignore a red check. Both facts belong in the lesson.
Repository: bcgov/lear-agentic.
This run shows the pipeline stopping, which is part of the result. Forty-one pull requests were closed
without merge, including a merge-train, and main was reset to upstream plus a fresh
enrolment. One pull request has been merged, and it is the enrolment itself. The first real slice
(CONFIG-003) reached a green build and waited for a person. Agents do not merge their own work. The rest
of the assessment backlog is still open. Treat LEAR as the control: a harness that can generate a backlog
still needs a named person who will accept the first change before a second one is worth starting.
Factory, on our code
The original service is
bcgov/digital_marketplace.
The experiment is
bcgov/digital-marketplace-next,
produced through
agentic-sdlc.
The repository keeps the brief, the recovered specification, the tests, the plan, the application, and
every gate ruling. The rebuilt application runs on a developer machine. There is no deployment stage.
The top navigation in the rebuild does not yet link the opportunity and organization lists; those routes
exist at /opportunities and /organizations.
The method is the part worth copying. Agents read the original and wrote acceptance criteria. Tests were derived from those criteria without seeing an implementation, then run against the original application so the tests had to describe what the system really does. The plan cut the criteria into 21 slices. A builder implemented each slice. Verify ran that slice’s tests and the earlier slices’ tests before a reviewer could approve. Routine rulings, such as a re-derived test, could be approved by automated checks. A person could take any seat.
| Measure | Result, counted from the published ledger on 4 Oct 2026 |
|---|---|
| Criteria recovered | 295 rows in the coverage table: 286 accepted, 9 obsolete, 3 still open questions. |
| Slices | All 21 slices have an approval in the gate ledger. First slice approval 25 Sep 2026, last slice approval 4 Oct 2026. About 9.8 days of calendar time. |
| Time per slice | Median gap between consecutive slice approvals is about 3.2 hours. The gap from slice 1 to slice 2 is about 142 hours. The early slices are where the specification and the test adapter were still being fought. |
| Gate rulings | 655 rulings: 464 approvals, 185 returns, 6 withdrawals. Of the 110 rulings on a build slice, 87 were returns. Work came back about four times per slice before it stayed approved. |
| Reviewer effort | 25 of the 655 rulings were a person, all in the tech-lead seat: 15 approvals and 10 returns. The other rulings were a persona agent or the runner. The ledger’s cost column on agent rulings sums to about $538, median about $0.61. That is the cost of the ruling step, not the cost of writing the slice. |
| Tests against the original | Full run on 4 Oct 2026: 199 pass, 44 fail, 7 unbound, 15 not testable, 265 rows. Of the tests that could run and bind, 81.9% passed. The 44 failures are information: the recovered criteria and the original system disagree, and someone has to decide which side is right. |
| Tests against the rebuild | A slice was approved when verify reported that every criterion the slice claimed was passing, including earlier slices. The run log uses that sentence for 15 of the 21 slices; the ledger shows an approval for all 21. There is no separate full-suite row for the new application in the results table. Do not read the 81.9% figure as the rebuild’s pass rate. That figure is the original system. |
The returns are the result, not a blemish. Slice 21 was sent back because criteria could not be exercised, then because they failed, then because an earlier slice’s criterion regressed, and only then verified clean. Slice 14 was refused when the builder was asked to change a design file the build stage is not allowed to write. The gate’s job is to send that condition to the stage that can carry it out.
What we brought home
These figures are Alberta’s, from the Velocity papers and from the August 2026 AI Academy Level 3 sessions this team attended. They are included so a BC team can see the shape of the pattern at estate scale. They are not measurements of the runs in the previous sections. Classroom figures from the Academy were spoken in the room; treat them as the presenter’s account until a paper or a dataset restates them.
The Garage points a harness at the existing estate. In the Academy walkthrough the sequence was: a Git Insights brief is a lead, not a verdict; a second scan (dependency and application checks, plus a red-team and blue-team pass) re-verifies before anyone edits; repositories that already have tests and a clean dependency bump are patched; repositories that are vendor-locked or tangled are routed to a rebuild instead. The presenter described one orchestrator driving 30 virtual machines and about 300 coding-agent instances, fixing on the order of 55,000 of about 79,000 known dependency findings in about 40 hours, at a stated compute cost of $125,000 (about $34 a repository) after a scan he put at about $10 a repository. He also said about 2,600 pull requests were staged and not merged, because the next missing piece was a smoke test good enough to hand to the team that owns the repository. About 73% of the repositories had no continuous integration, so the harness was adding tests in the same pass that claimed a fix.
The lesson we kept is the one our own re-checks confirmed at a much smaller scale. A scan starts the work. A second check, against the original wording of the finding, is what you are allowed to call a fix. And a pull request that the owning team has not accepted is still a proposal.
The Factory builds new. Alberta’s published case study, From Five Months to Four Days, gives two numbers from their own staff. The Remote Area Heating Allowance application, hand-written in Java over five months in the early 2000s, was rebuilt in four days with a public portal the original did not have. The Alberta Classroom Information Portal was delivered in eleven weeks for about $108,000, against a traditional estimate of $1.3 million to $1.9 million and up to eighteen months. The paper summarises the pair as a 25-fold speed change and a 95% drop in delivery cost. Those are Alberta’s delivery accounts, with their own baselines.
The Academy also showed a smaller version of the same idea: several agents building the same specification in parallel, so a mistake that appears in every build is blamed on the template rather than on one model. That exercise produced a working application in a teaching session. It was not a production launch. Our factory-shaped run is the Digital Marketplace rebuild above: one specification, tests locked to the original behaviour, slices, and a gate that returns work. We have not reproduced a four-day or an eleven-week production cutover, and this page does not claim one.
| If you are choosing | Start here |
|---|---|
| The defect is local, tests exist or can be added, and someone owns the merge. | Garage. File the finding, sign a slice no wider than you will verify, and re-read the original ticket after merge. |
| The repository is the product, and patching it would preserve the tangle. | Factory. Recover acceptance criteria, prove the tests on the old system, then build the new one in slices the gate can refuse. |
| You only have a vulnerability report. | Stop and find the owner. Sightline, and Alberta’s Git Insights the same way, will hand you a repository with nobody’s name on it. |
Worth keeping
Specification, plan, implementation. On restitution that produced exactly 108 merged pull requests for 36 findings, and the independent re-check found no empty or wrong diff hiding behind a closed issue.
On the Marketplace rebuild, 87 of 110 build-slice rulings were returns. Slices that could not be exercised, or that broke an earlier slice, did not get an approval until verify was clean.
Twenty-five human rulings out of 655, ten of them returns, were enough to steer the rebuild. The pattern holds when the person sees the refusals and the scope changes, and stays off the routine test re-derivation.
Parks was 35 fixed, 8 partial, 2 not fixed. Restitution was 35 fixed, 1 partial, 0 not fixed. Those counts are the ones to quote. “45 of 45 issues closed” overstates what the code does.
A recommended security backlog of this size moved in about a day of wall clock once the harness, the assessment, and a person who would accept merges were already in place. Setup and the first accepted slice are the long part. LEAR is still in that part.
Marketplace tests were derived from criteria and run on the original system first. Disagreement (44 failing tests on the original) showed up before the rebuild could paper over it.
Worth avoiding
| What happened | Where | What to do on the next run |
|---|---|---|
| An evidence file described a fix the pull request did not contain. | Parks, LOG-002. The implementation pull request changed docs/pr-evidence.md only. |
Refuse an implementation pull request that does not touch a path named in the ticket or the feature file. |
| The signed slice was narrower than the assessment, and the pipeline then reported the finding shipped. | Parks logging and test findings; restitution VULN-004. | When the specification drops part of the original expected result, write that down as an accepted gap and leave a follow-up. Keep it out of the “fixed” tally. |
GitHub Actions sat on action_required until a person pushed an empty commit. |
Restitution, repeated from the first slices. | Budget operator time for workflow approval, or pre-approve the agent’s identity. This ate more human attention than reading the diffs. |
| A standing CodeQL failure trained the run to ignore a red gate. | Restitution, from the middle of the backlog onward. | Split the security checks so a known residual cannot mask a new Trivy or test failure. |
| Coding-agent credits ran out, and the operator became the builder. | Restitution, findings 30 through 36. | Put a project budget on the run before the first slice. A backlog that depends on a monthly seat cap will stop in the middle. |
| A generated backlog was closed without merge. | LEAR. 41 pull requests closed, one enrolment merge, the first real slice waiting on a person. | Do not open the second slice until a named owner has merged the first. Agents are instructed not to merge themselves, and that rule is the point. |
| The receipt collector reported missing reviews that were actually there. | Parks. Review-receipt miss rate sat at 100%, then 75%, because the collector skipped evidence files when the pull request title lacked a tag. | Fix the measure before you manage to it. A red metric that is a parser bug will get ignored, and then a real miss will too. |
| The rebuild’s results table has a full run for the original system and none for the new one. | Marketplace, results ledger, 4 Oct 2026. | Publish one end-to-end pass/fail row for the new application beside the original. Slice-level “every claimed criterion passes” is necessary and is not that row. |
External case
This work belongs to Rogene Lacanienta’s team at Transportation and Transit. It is the external case we most wanted in a paper other teams could use: a Java 8 Struts system, lifted with agents, with a person in the centre of the loop. This page does not describe their method, their timeline, or their results. Those details stay with the team that did the work until Rogene agrees they can be published.
For another team
Start from a repository you can afford to be wrong about. Fork it, or rebuild it beside the original. Enrol the pipeline, file one finding or one slice, and merge it yourself before you let a backlog run. The public repositories below are the ones that were public on 9 October 2026.
| Repository | What you will find |
|---|---|
| alex-struk/agentic-sdlc | The pipeline the Marketplace rebuild installed: skills, checks, workflows, and a runner a project takes by reference. |
| bcgov/digital-marketplace-next | The greenfield record. Read the README, then spec/, plan/tasks.md, and site/gates.md. Local run instructions are in the README. Invented seed data only. |
| bcgov/bcparks-ar-admin-agentic-b | The completed parks remediation, including the metrics issues and the evidence-only miss. |
| bcgov/pssg-cscp-restitution-agentic | The restitution remediation on development, including the operator adjustments in the pull request history. |
| bcgov/lear-agentic | The run that stopped at the first human merge. Useful before you scale a backlog. |
| bcgov/tier1-pattern-test | A small public enrolment of the thinner automation: issue triage, docs drift, and a comment on a failed build. |
| bcgov/tier2-pattern-test | A small public enrolment of the checkpointed pattern, for teams that want the gates before they want a full rebuild. |
Alberta’s papers, if you want the pattern in their words: the four approaches and the factory case study.
A reasonable first week is one repository, one owner, and one finding that already has a test. If that pull request cannot be reviewed by someone who would be on the hook in production, the rest of the backlog will reproduce LEAR: a queue of proposals and a single merged enrolment.